ruboto 0.6.0 → 0.7.0

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 (73) hide show
  1. data/README.md +13 -49
  2. data/Rakefile +26 -26
  3. data/assets/rakelib/ruboto.rake +29 -9
  4. data/assets/res/drawable-hdpi/{icon.png → ic_launcher.png} +0 -0
  5. data/assets/res/drawable-ldpi/{icon.png → ic_launcher.png} +0 -0
  6. data/assets/res/drawable-mdpi/{icon.png → ic_launcher.png} +0 -0
  7. data/assets/samples/sample_activity.rb +19 -9
  8. data/assets/samples/sample_broadcast_receiver.rb +3 -1
  9. data/assets/samples/sample_service.rb +9 -9
  10. data/assets/src/InheritingActivity.java +1 -1
  11. data/assets/src/InheritingBroadcastReceiver.java +4 -4
  12. data/assets/src/InheritingClass.java +1 -1
  13. data/assets/src/InheritingService.java +2 -1
  14. data/assets/src/RubotoActivity.java +26 -17
  15. data/assets/src/RubotoBroadcastReceiver.java +32 -11
  16. data/assets/src/RubotoService.java +23 -13
  17. data/assets/src/org/ruboto/EntryPointActivity.java +18 -26
  18. data/assets/src/org/ruboto/JRubyAdapter.java +468 -0
  19. data/assets/src/org/ruboto/Log.java +22 -0
  20. data/assets/src/org/ruboto/Script.java +113 -587
  21. data/assets/src/org/ruboto/test/ActivityTest.java +7 -7
  22. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +27 -12
  23. data/assets/src/ruboto.rb +1 -1
  24. data/assets/src/ruboto/activity.rb +10 -1
  25. data/assets/src/ruboto/base.rb +0 -12
  26. data/assets/src/ruboto/broadcast_receiver.rb +12 -0
  27. data/assets/src/ruboto/menu.rb +0 -1
  28. data/assets/src/ruboto/package.rb +11 -0
  29. data/assets/src/ruboto/service.rb +9 -0
  30. data/lib/ruboto/commands/base.rb +1 -1
  31. data/lib/ruboto/util/build.rb +1 -1
  32. data/lib/ruboto/util/update.rb +35 -27
  33. data/lib/ruboto/util/xml_element.rb +25 -12
  34. data/lib/ruboto/version.rb +2 -2
  35. data/test/activity/image_button_activity.rb +14 -11
  36. data/test/activity/image_button_activity_test.rb +2 -6
  37. data/test/activity/image_button_and_button_activity.rb +15 -17
  38. data/test/activity/image_button_and_button_activity_test.rb +4 -8
  39. data/test/activity/option_menu_activity.rb +17 -12
  40. data/test/activity/option_menu_activity_test.rb +1 -4
  41. data/test/activity/psych_activity.rb +20 -13
  42. data/test/activity/psych_activity_test.rb +3 -1
  43. data/test/activity/stack_activity.rb +17 -14
  44. data/test/activity/stack_activity_test.rb +13 -12
  45. data/test/app_test_methods.rb +25 -19
  46. data/test/block_def_activity/image_button_activity.rb +23 -0
  47. data/test/block_def_activity/image_button_activity_test.rb +21 -0
  48. data/test/block_def_activity/image_button_and_button_activity.rb +20 -0
  49. data/test/block_def_activity/image_button_and_button_activity_test.rb +27 -0
  50. data/test/block_def_activity/option_menu_activity.rb +26 -0
  51. data/test/block_def_activity/option_menu_activity_test.rb +18 -0
  52. data/test/block_def_activity/psych_activity.rb +35 -0
  53. data/test/block_def_activity/psych_activity_test.rb +16 -0
  54. data/test/block_def_activity/stack_activity.rb +25 -0
  55. data/test/block_def_activity/stack_activity_test.rb +31 -0
  56. data/test/broadcast_receiver_test.rb +2 -2
  57. data/test/handle_activity/image_button_activity.rb +21 -0
  58. data/test/handle_activity/image_button_activity_test.rb +21 -0
  59. data/test/handle_activity/image_button_and_button_activity.rb +24 -0
  60. data/test/handle_activity/image_button_and_button_activity_test.rb +27 -0
  61. data/test/handle_activity/option_menu_activity.rb +21 -0
  62. data/test/handle_activity/option_menu_activity_test.rb +20 -0
  63. data/test/handle_activity/psych_activity.rb +31 -0
  64. data/test/handle_activity/psych_activity_test.rb +16 -0
  65. data/test/handle_activity/stack_activity.rb +21 -0
  66. data/test/handle_activity/stack_activity_test.rb +32 -0
  67. data/test/minimal_app_test.rb +4 -4
  68. data/test/rake_test.rb +15 -1
  69. data/test/ruboto_gen_test.rb +7 -4
  70. data/test/service_test.rb +110 -21
  71. data/test/test_helper.rb +17 -14
  72. data/test/updated_example_test_methods.rb +5 -14
  73. metadata +30 -7
@@ -4,7 +4,7 @@ import java.io.IOException;
4
4
 
5
5
  public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
6
6
  private String scriptName = null;
7
- private boolean initialized = false;
7
+ private Object rubyInstance;
8
8
 
9
9
  public void setCallbackProc(int id, Object obj) {
10
10
  // Error: no callbacks
@@ -25,17 +25,33 @@ public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
25
25
  if (name != null) {
26
26
  setScriptName(name);
27
27
 
28
- if (Script.isInitialized()) {
28
+ if (JRubyAdapter.isInitialized()) {
29
29
  loadScript();
30
30
  }
31
31
  }
32
32
  }
33
33
 
34
34
  protected void loadScript() {
35
- Script.put("$broadcast_receiver", this);
35
+
36
+ // TODO(uwe): Only needed for non-class-based definitions
37
+ // Can be removed if we stop supporting non-class-based definitions
38
+ JRubyAdapter.put("$broadcast_receiver", this);
39
+ // TODO end
40
+
36
41
  if (scriptName != null) {
37
42
  try {
38
- new Script(scriptName).execute();
43
+ String rubyClassName = Script.toCamelCase(scriptName);
44
+ System.out.println("Looking for Ruby class: " + rubyClassName);
45
+ Object rubyClass = JRubyAdapter.get(rubyClassName);
46
+ if (rubyClass == null) {
47
+ System.out.println("Loading script: " + scriptName);
48
+ JRubyAdapter.exec(new Script(scriptName).getContents());
49
+ rubyClass = JRubyAdapter.get(rubyClassName);
50
+ }
51
+ if (rubyClass != null) {
52
+ System.out.println("Instanciating Ruby class: " + rubyClassName);
53
+ rubyInstance = JRubyAdapter.callMethod(rubyClass, "new", this, Object.class);
54
+ }
39
55
  } catch(IOException e) {
40
56
  throw new RuntimeException("IOException loading broadcast receiver script", e);
41
57
  }
@@ -43,16 +59,21 @@ public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
43
59
  }
44
60
 
45
61
  public void onReceive(android.content.Context context, android.content.Intent intent) {
46
- Script.put("$context", context);
47
- Script.put("$broadcast_receiver", this);
48
- Script.put("$intent", intent);
49
-
50
62
  try {
51
- Script.execute("$broadcast_receiver.on_receive($context, $intent)");
63
+ System.out.println("onReceive: " + rubyInstance);
64
+ if (rubyInstance != null) {
65
+ JRubyAdapter.callMethod(rubyInstance, "on_receive", new Object[]{context, intent});
66
+ } else {
67
+ // TODO(uwe): Only needed for non-class-based definitions
68
+ // Can be removed if we stop supporting non-class-based definitions
69
+ JRubyAdapter.put("$context", context);
70
+ JRubyAdapter.put("$broadcast_receiver", this);
71
+ JRubyAdapter.put("$intent", intent);
72
+ JRubyAdapter.execute("$broadcast_receiver.on_receive($context, $intent)");
73
+ // TODO end
74
+ }
52
75
  } catch(Exception e) {
53
76
  e.printStackTrace();
54
77
  }
55
78
  }
56
79
  }
57
-
58
-
@@ -2,12 +2,11 @@ package THE_PACKAGE;
2
2
 
3
3
  import org.ruboto.Script;
4
4
  import java.io.IOException;
5
- import android.app.ProgressDialog;
6
5
 
7
6
  public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
8
7
  private String scriptName;
9
- private String remoteVariable = "";
10
8
  public Object[] args;
9
+ private Object rubyInstance;
11
10
 
12
11
  THE_CONSTANTS
13
12
 
@@ -17,11 +16,6 @@ THE_CONSTANTS
17
16
  callbackProcs[id] = obj;
18
17
  }
19
18
 
20
- public THE_RUBOTO_CLASS setRemoteVariable(String var) {
21
- remoteVariable = ((var == null) ? "" : (var + "."));
22
- return this;
23
- }
24
-
25
19
  public void setScriptName(String name){
26
20
  scriptName = name;
27
21
  }
@@ -33,20 +27,36 @@ THE_CONSTANTS
33
27
 
34
28
  @Override
35
29
  public void onCreate() {
30
+ System.out.println("RubotoService.onCreate()");
36
31
  args = new Object[0];
37
32
 
38
33
  super.onCreate();
39
34
 
40
- if (Script.setUpJRuby(this)) {
41
- Script.defineGlobalVariable("$context", this);
42
- Script.defineGlobalVariable("$service", this);
35
+ if (JRubyAdapter.setUpJRuby(this)) {
36
+ // TODO(uwe): Only needed for non-class-based definitions
37
+ // Can be removed if we stop supporting non-class-based definitions
38
+ JRubyAdapter.defineGlobalVariable("$context", this);
39
+ JRubyAdapter.defineGlobalVariable("$service", this);
40
+ // TODO end
43
41
 
44
42
  try {
45
43
  if (scriptName != null) {
46
- new Script(scriptName).execute();
44
+ String rubyClassName = Script.toCamelCase(scriptName);
45
+ System.out.println("Looking for Ruby class: " + rubyClassName);
46
+ Object rubyClass = JRubyAdapter.get(rubyClassName);
47
+ if (rubyClass == null) {
48
+ System.out.println("Loading script: " + scriptName);
49
+ JRubyAdapter.exec(new Script(scriptName).getContents());
50
+ rubyClass = JRubyAdapter.get(rubyClassName);
51
+ }
52
+ if (rubyClass != null) {
53
+ System.out.println("Instanciating Ruby class: " + rubyClassName);
54
+ rubyInstance = JRubyAdapter.callMethod(rubyClass, "new", this, Object.class);
55
+ JRubyAdapter.callMethod(rubyInstance, "on_create");
56
+ }
47
57
  } else {
48
- Script.execute("$service.initialize_ruboto");
49
- Script.execute("$service.on_create");
58
+ JRubyAdapter.execute("$service.initialize_ruboto");
59
+ JRubyAdapter.execute("$service.on_create");
50
60
  }
51
61
  } catch(IOException e) {
52
62
  e.printStackTrace();
@@ -1,10 +1,5 @@
1
1
  package org.ruboto;
2
2
 
3
- import java.io.File;
4
- import java.io.IOException;
5
-
6
- import org.ruboto.Script;
7
-
8
3
  import android.app.ProgressDialog;
9
4
  import android.content.BroadcastReceiver;
10
5
  import android.content.Context;
@@ -14,8 +9,6 @@ import android.content.Intent;
14
9
  import android.content.IntentFilter;
15
10
  import android.net.Uri;
16
11
  import android.os.Bundle;
17
- import android.os.Handler;
18
- import android.util.Log;
19
12
  import android.view.View;
20
13
  import android.widget.TextView;
21
14
  import android.widget.Toast;
@@ -28,7 +21,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
28
21
  protected boolean appStarted = false;
29
22
 
30
23
  public void onCreate(Bundle bundle) {
31
- Log.d("RUBOTO", "onCreate: ");
24
+ Log.d("onCreate: ");
32
25
 
33
26
  try {
34
27
  splash = Class.forName(getPackageName() + ".R$layout").getField("splash").getInt(null);
@@ -36,32 +29,32 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
36
29
  splash = -1;
37
30
  }
38
31
 
39
- if (Script.isInitialized()) {
32
+ if (JRubyAdapter.isInitialized()) {
40
33
  appStarted = true;
41
34
  }
42
35
  super.onCreate(bundle);
43
36
  }
44
37
 
45
38
  public void onResume() {
46
- Log.d("RUBOTO", "onResume: ");
39
+ Log.d("onResume: ");
47
40
 
48
41
  if(appStarted) {
49
- Log.d("RUBOTO", "onResume: App already started!");
42
+ Log.d("onResume: App already started!");
50
43
  super.onResume();
51
44
  return;
52
45
  }
53
46
 
54
- Log.d("RUBOTO", "onResume: Checking JRuby");
55
- if (Script.isInitialized()) {
56
- Log.d("RUBOTO", "Already initialized");
47
+ Log.d("onResume: Checking JRuby");
48
+ if (JRubyAdapter.isInitialized()) {
49
+ Log.d("Already initialized");
57
50
  fireRubotoActivity();
58
51
  } else {
59
- Log.d("RUBOTO", "Not initialized");
52
+ Log.d("Not initialized");
60
53
  showProgress();
61
54
  receiver = new BroadcastReceiver(){
62
55
  public void onReceive(Context context, Intent intent) {
63
- Log.i("RUBOTO", "received broadcast: " + intent);
64
- Log.i("RUBOTO", "URI: " + intent.getData());
56
+ Log.i("received broadcast: " + intent);
57
+ Log.i("URI: " + intent.getData());
65
58
  if (intent.getData().toString().equals("package:org.ruboto.core")) {
66
59
  Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
67
60
  if (receiver != null) {
@@ -82,7 +75,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
82
75
  }
83
76
 
84
77
  public void onPause() {
85
- Log.d("RUBOTO", "onPause: ");
78
+ Log.d("onPause: ");
86
79
 
87
80
  if (receiver != null) {
88
81
  unregisterReceiver(receiver);
@@ -92,7 +85,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
92
85
  }
93
86
 
94
87
  public void onDestroy() {
95
- Log.d("RUBOTO", "onDestroy: ");
88
+ Log.d("onDestroy: ");
96
89
 
97
90
  super.onDestroy();
98
91
  if (dialogCancelled) {
@@ -104,9 +97,9 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
104
97
  private void initJRuby(final boolean firstTime) {
105
98
  new Thread(new Runnable() {
106
99
  public void run() {
107
- final boolean jrubyOk = Script.setUpJRuby(EntryPointActivity.this);
100
+ final boolean jrubyOk = JRubyAdapter.setUpJRuby(EntryPointActivity.this);
108
101
  if (jrubyOk) {
109
- Log.d("RUBOTO", "onResume: JRuby OK");
102
+ Log.d("onResume: JRuby OK");
110
103
  prepareJRuby();
111
104
  runOnUiThread(new Runnable() {
112
105
  public void run() {
@@ -117,7 +110,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
117
110
  runOnUiThread(new Runnable() {
118
111
  public void run() {
119
112
  if (firstTime) {
120
- Log.d("RUBOTO", "onResume: Checking JRuby - IN UI thread");
113
+ Log.d("onResume: Checking JRuby - IN UI thread");
121
114
  try {
122
115
  setContentView(Class.forName(getPackageName() + ".R$layout").getField("get_ruboto_core").getInt(null));
123
116
  } catch (Exception e) {
@@ -147,7 +140,6 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
147
140
  startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.ruboto.core")));
148
141
  } catch (android.content.ActivityNotFoundException anfe) {
149
142
  try {
150
- TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
151
143
  Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(RUBOTO_URL));
152
144
  startActivity(intent);
153
145
  } catch (Exception e) {}
@@ -157,7 +149,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
157
149
  protected void fireRubotoActivity() {
158
150
  if(appStarted) return;
159
151
  appStarted = true;
160
- Log.i("RUBOTO", "Starting activity");
152
+ Log.i("Starting activity");
161
153
  loadScript();
162
154
  onStart();
163
155
  super.onResume();
@@ -166,7 +158,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
166
158
 
167
159
  private void showProgress() {
168
160
  if (loadingDialog == null) {
169
- Log.i("RUBOTO", "Showing progress");
161
+ Log.i("Showing progress");
170
162
  if (splash > 0) {
171
163
  requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
172
164
  setContentView(splash);
@@ -185,7 +177,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
185
177
 
186
178
  private void hideProgress() {
187
179
  if (loadingDialog != null) {
188
- Log.d("RUBOTO", "Hide progress");
180
+ Log.d("Hide progress");
189
181
  loadingDialog.dismiss();
190
182
  loadingDialog = null;
191
183
  }
@@ -0,0 +1,468 @@
1
+ package org.ruboto;
2
+
3
+ import java.io.File;
4
+ import java.io.FilenameFilter;
5
+ import java.io.PrintStream;
6
+ import java.lang.reflect.InvocationTargetException;
7
+ import java.lang.reflect.Method;
8
+
9
+ import android.content.Context;
10
+ import android.content.pm.ApplicationInfo;
11
+ import android.content.pm.PackageInfo;
12
+ import android.content.pm.PackageManager;
13
+ import android.content.pm.PackageManager.NameNotFoundException;
14
+ import android.os.Environment;
15
+ import dalvik.system.PathClassLoader;
16
+
17
+ public class JRubyAdapter {
18
+ private static Object ruby;
19
+ private static boolean isDebugBuild = false;
20
+ private static PrintStream output = null;
21
+ private static boolean initialized = false;
22
+ private static String localContextScope = "SINGLETON";
23
+ private static String localVariableBehavior = "TRANSIENT";
24
+ private static String RUBOTO_CORE_VERSION_NAME;
25
+
26
+ public static void callMethod(Object receiver, String methodName, Object[] args) {
27
+ try {
28
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object[].class);
29
+ callMethodMethod.invoke(ruby, receiver, methodName, args);
30
+ } catch (NoSuchMethodException nsme) {
31
+ throw new RuntimeException(nsme);
32
+ } catch (IllegalAccessException iae) {
33
+ throw new RuntimeException(iae);
34
+ } catch (java.lang.reflect.InvocationTargetException ite) {
35
+ printStackTrace(ite);
36
+ if (isDebugBuild) {
37
+ throw new RuntimeException(ite);
38
+ }
39
+ }
40
+ }
41
+
42
+ public static void callMethod(Object object, String methodName, Object arg) {
43
+ callMethod(object, methodName, new Object[] { arg });
44
+ }
45
+
46
+ public static void callMethod(Object object, String methodName) {
47
+ callMethod(object, methodName, new Object[] {});
48
+ }
49
+
50
+ @SuppressWarnings("unchecked")
51
+ public static <T> T callMethod(Object receiver, String methodName, Object[] args, Class<T> returnType) {
52
+ try {
53
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object[].class, Class.class);
54
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, args, returnType);
55
+ } catch (NoSuchMethodException nsme) {
56
+ throw new RuntimeException(nsme);
57
+ } catch (IllegalAccessException iae) {
58
+ throw new RuntimeException(iae);
59
+ } catch (java.lang.reflect.InvocationTargetException ite) {
60
+ printStackTrace(ite);
61
+ }
62
+ return null;
63
+ }
64
+
65
+ @SuppressWarnings("unchecked")
66
+ public static <T> T callMethod(Object receiver, String methodName, Object arg, Class<T> returnType) {
67
+ try {
68
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object.class, Class.class);
69
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, arg, returnType);
70
+ } catch (NoSuchMethodException nsme) {
71
+ throw new RuntimeException(nsme);
72
+ } catch (IllegalAccessException iae) {
73
+ throw new RuntimeException(iae);
74
+ } catch (java.lang.reflect.InvocationTargetException ite) {
75
+ printStackTrace(ite);
76
+ }
77
+ return null;
78
+ }
79
+
80
+ @SuppressWarnings("unchecked")
81
+ public static <T> T callMethod(Object receiver, String methodName, Class<T> returnType) {
82
+ try {
83
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Class.class);
84
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, returnType);
85
+ } catch (NoSuchMethodException nsme) {
86
+ throw new RuntimeException(nsme);
87
+ } catch (IllegalAccessException iae) {
88
+ throw new RuntimeException(iae);
89
+ } catch (java.lang.reflect.InvocationTargetException ite) {
90
+ printStackTrace(ite);
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #put(String name, Object object)}
97
+ */
98
+ @Deprecated public static void defineGlobalConstant(String name, Object object) {
99
+ put(name, object);
100
+ }
101
+
102
+ /**
103
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #put(String name, Object object)}
104
+ */
105
+ @Deprecated public static void defineGlobalVariable(String name, Object object) {
106
+ put(name, object);
107
+ }
108
+
109
+ /**
110
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #runScriptlet(String code)}
111
+ */
112
+ @Deprecated public static Object exec(String code) {
113
+ try {
114
+ Method runScriptletMethod = ruby.getClass().getMethod("runScriptlet", String.class);
115
+ return runScriptletMethod.invoke(ruby, code);
116
+ } catch (NoSuchMethodException nsme) {
117
+ throw new RuntimeException(nsme);
118
+ } catch (IllegalAccessException iae) {
119
+ throw new RuntimeException(iae);
120
+ } catch (java.lang.reflect.InvocationTargetException ite) {
121
+ if (isDebugBuild) {
122
+ throw ((RuntimeException) ite.getCause());
123
+ } else {
124
+ return null;
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #runScriptlet(String code)}
131
+ */
132
+ @Deprecated public static String execute(String code) {
133
+ Object result = exec(code);
134
+ return result != null ? result.toString() : "nil";
135
+ // TODO: Why is callMethod returning "main"?
136
+ // return result != null ? callMethod(result, "inspect", String.class) : "null";
137
+ }
138
+
139
+ public static Object get(String name) {
140
+ try {
141
+ Method getMethod = ruby.getClass().getMethod("get", String.class);
142
+ return getMethod.invoke(ruby, name);
143
+ } catch (NoSuchMethodException nsme) {
144
+ throw new RuntimeException(nsme);
145
+ } catch (IllegalAccessException iae) {
146
+ throw new RuntimeException(iae);
147
+ } catch (java.lang.reflect.InvocationTargetException ite) {
148
+ throw new RuntimeException(ite);
149
+ }
150
+ }
151
+
152
+ public static String getPlatformVersionName() {
153
+ return RUBOTO_CORE_VERSION_NAME;
154
+ }
155
+
156
+ public static String getScriptFilename() {
157
+ return callScriptingContainerMethod(String.class, "getScriptFilename");
158
+ }
159
+
160
+ public static boolean isDebugBuild() {
161
+ return isDebugBuild;
162
+ }
163
+
164
+ public static synchronized boolean isInitialized() {
165
+ return initialized;
166
+ }
167
+
168
+ public static void put(String name, Object object) {
169
+ try {
170
+ Method putMethod = ruby.getClass().getMethod("put", String.class, Object.class);
171
+ putMethod.invoke(ruby, name, object);
172
+ } catch (NoSuchMethodException nsme) {
173
+ throw new RuntimeException(nsme);
174
+ } catch (IllegalAccessException iae) {
175
+ throw new RuntimeException(iae);
176
+ } catch (java.lang.reflect.InvocationTargetException ite) {
177
+ throw new RuntimeException(ite);
178
+ }
179
+ }
180
+
181
+ public static Object runScriptlet(String code) {
182
+ try {
183
+ Method runScriptletMethod = ruby.getClass().getMethod("runScriptlet", String.class);
184
+ return runScriptletMethod.invoke(ruby, code);
185
+ } catch (NoSuchMethodException nsme) {
186
+ throw new RuntimeException(nsme);
187
+ } catch (IllegalAccessException iae) {
188
+ throw new RuntimeException(iae);
189
+ } catch (java.lang.reflect.InvocationTargetException ite) {
190
+ if (isDebugBuild) {
191
+ throw ((RuntimeException) ite.getCause());
192
+ } else {
193
+ return null;
194
+ }
195
+ }
196
+ }
197
+
198
+ public static synchronized boolean setUpJRuby(Context appContext) {
199
+ return setUpJRuby(appContext, output == null ? System.out : output);
200
+ }
201
+
202
+ @SuppressWarnings({ "unchecked", "rawtypes" })
203
+ public static synchronized boolean setUpJRuby(Context appContext, PrintStream out) {
204
+ if (!initialized) {
205
+ // BEGIN Ruboto HeapAlloc
206
+ @SuppressWarnings("unused")
207
+ byte[] arrayForHeapAllocation = new byte[13 * 1024 * 1024];
208
+ arrayForHeapAllocation = null;
209
+ // END Ruboto HeapAlloc
210
+ setDebugBuild(appContext);
211
+ Log.d("Setting up JRuby runtime (" + (isDebugBuild ? "DEBUG" : "RELEASE") + ")");
212
+ System.setProperty("jruby.bytecode.version", "1.6");
213
+ System.setProperty("jruby.interfaces.useProxy", "true");
214
+ System.setProperty("jruby.management.enabled", "false");
215
+ System.setProperty("jruby.objectspace.enabled", "false");
216
+ System.setProperty("jruby.thread.pooling", "true");
217
+ System.setProperty("jruby.native.enabled", "false");
218
+ // System.setProperty("jruby.compat.version", "RUBY1_8"); // RUBY1_9 is the default
219
+
220
+ // Uncomment these to debug Ruby source loading
221
+ // System.setProperty("jruby.debug.loadService", "true");
222
+ // System.setProperty("jruby.debug.loadService.timing", "true");
223
+
224
+
225
+ ClassLoader classLoader;
226
+ Class<?> scriptingContainerClass;
227
+ String apkName = null;
228
+
229
+ try {
230
+ scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer");
231
+ System.out.println("Found JRuby in this APK");
232
+ classLoader = JRubyAdapter.class.getClassLoader();
233
+ try {
234
+ apkName = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0).sourceDir;
235
+ } catch (NameNotFoundException e) {}
236
+ } catch (ClassNotFoundException e1) {
237
+ String packageName = "org.ruboto.core";
238
+ try {
239
+ PackageInfo pkgInfo = appContext.getPackageManager().getPackageInfo(packageName, 0);
240
+ apkName = pkgInfo.applicationInfo.sourceDir;
241
+ RUBOTO_CORE_VERSION_NAME = pkgInfo.versionName;
242
+ } catch (PackageManager.NameNotFoundException e2) {
243
+ out.println("JRuby not found in local APK:");
244
+ e1.printStackTrace(out);
245
+ out.println("JRuby not found in platform APK:");
246
+ e2.printStackTrace(out);
247
+ return false;
248
+ }
249
+
250
+ System.out.println("Found JRuby in platform APK");
251
+ classLoader = new PathClassLoader(apkName, JRubyAdapter.class.getClassLoader());
252
+
253
+ try {
254
+ scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
255
+ } catch (ClassNotFoundException e) {
256
+ // FIXME(uwe): ScriptingContainer not found in the platform APK...
257
+ e.printStackTrace();
258
+ return false;
259
+ }
260
+ }
261
+
262
+ try {
263
+ Class scopeClass = Class.forName("org.jruby.embed.LocalContextScope", true, scriptingContainerClass.getClassLoader());
264
+ Class behaviorClass = Class.forName("org.jruby.embed.LocalVariableBehavior", true, scriptingContainerClass.getClassLoader());
265
+
266
+ ruby = scriptingContainerClass
267
+ .getConstructor(scopeClass, behaviorClass)
268
+ .newInstance(Enum.valueOf(scopeClass, localContextScope),
269
+ Enum.valueOf(behaviorClass, localVariableBehavior));
270
+
271
+ Class compileModeClass = Class.forName("org.jruby.RubyInstanceConfig$CompileMode", true, classLoader);
272
+ callScriptingContainerMethod(Void.class, "setCompileMode", Enum.valueOf(compileModeClass, "OFF"));
273
+
274
+ // Class traceTypeClass = Class.forName("org.jruby.runtime.backtrace.TraceType", true, classLoader);
275
+ // Method traceTypeForMethod = traceTypeClass.getMethod("traceTypeFor", String.class);
276
+ // Object traceTypeRaw = traceTypeForMethod.invoke(null, "raw");
277
+ // callScriptingContainerMethod(Void.class, "setTraceType", traceTypeRaw);
278
+
279
+ // FIXME(uwe): Write tutorial on profiling.
280
+ // container.getProvider().getRubyInstanceConfig().setProfilingMode(mode);
281
+
282
+ // callScriptingContainerMethod(Void.class, "setClassLoader", classLoader);
283
+ Method setClassLoaderMethod = ruby.getClass().getMethod("setClassLoader", ClassLoader.class);
284
+ setClassLoaderMethod.invoke(ruby, classLoader);
285
+
286
+ Thread.currentThread().setContextClassLoader(classLoader);
287
+
288
+ String defaultCurrentDir = appContext.getFilesDir().getPath();
289
+ Log.d("Setting JRuby current directory to " + defaultCurrentDir);
290
+ callScriptingContainerMethod(Void.class, "setCurrentDirectory", defaultCurrentDir);
291
+
292
+ if (out != null) {
293
+ output = out;
294
+ setOutputStream(out);
295
+ } else if (output != null) {
296
+ setOutputStream(output);
297
+ }
298
+
299
+ String jrubyHome = "file:" + apkName + "!";
300
+ Log.i("Setting JRUBY_HOME: " + jrubyHome);
301
+ System.setProperty("jruby.home", jrubyHome);
302
+
303
+ addLoadPath(scriptsDirName(appContext));
304
+
305
+ initialized = true;
306
+ } catch (ClassNotFoundException e) {
307
+ handleInitException(e);
308
+ } catch (IllegalArgumentException e) {
309
+ handleInitException(e);
310
+ } catch (SecurityException e) {
311
+ handleInitException(e);
312
+ } catch (InstantiationException e) {
313
+ handleInitException(e);
314
+ } catch (IllegalAccessException e) {
315
+ handleInitException(e);
316
+ } catch (InvocationTargetException e) {
317
+ handleInitException(e);
318
+ } catch (NoSuchMethodException e) {
319
+ handleInitException(e);
320
+ }
321
+ }
322
+ return initialized;
323
+ }
324
+
325
+ public static void setScriptFilename(String name) {
326
+ callScriptingContainerMethod(Void.class, "setScriptFilename", name);
327
+ }
328
+
329
+ public static boolean usesPlatformApk() {
330
+ return RUBOTO_CORE_VERSION_NAME != null;
331
+ }
332
+
333
+ // Private methods
334
+
335
+ private static Boolean addLoadPath(String scriptsDir) {
336
+ if (new File(scriptsDir).exists()) {
337
+ Log.i("Added directory to load path: " + scriptsDir);
338
+ Script.addDir(scriptsDir);
339
+ runScriptlet("$:.unshift '" + scriptsDir + "' ; $:.uniq!");
340
+ Log.d("Changing JRuby current directory to " + scriptsDir);
341
+ callScriptingContainerMethod(Void.class, "setCurrentDirectory", scriptsDir);
342
+ return true;
343
+ } else {
344
+ Log.i("Extra scripts dir not present: " + scriptsDir);
345
+ return false;
346
+ }
347
+ }
348
+
349
+ @SuppressWarnings("unchecked")
350
+ private static <T> T callScriptingContainerMethod(Class<T> returnType, String methodName, Object... args) {
351
+ Class<?>[] argClasses = new Class[args.length];
352
+ for (int i = 0; i < argClasses.length; i++) {
353
+ argClasses[i] = args[i].getClass();
354
+ }
355
+ try {
356
+ Method method = ruby.getClass().getMethod(methodName, argClasses);
357
+ T result = (T) method.invoke(ruby, args);
358
+ return result;
359
+ } catch (RuntimeException re) {
360
+ re.printStackTrace();
361
+ } catch (IllegalAccessException e) {
362
+ e.printStackTrace();
363
+ } catch (InvocationTargetException e) {
364
+ printStackTrace(e);
365
+ } catch (NoSuchMethodException e) {
366
+ e.printStackTrace();
367
+ }
368
+ return null;
369
+ }
370
+
371
+ private static void handleInitException(Exception e) {
372
+ Log.e("Exception starting JRuby");
373
+ Log.e(e.getMessage() != null ? e.getMessage() : e.getClass().getName());
374
+ e.printStackTrace();
375
+ ruby = null;
376
+ }
377
+
378
+ static void printStackTrace(Throwable t) {
379
+ // TODO(uwe): Simplify this when Issue #144 is resolved
380
+ try {
381
+ t.printStackTrace(output);
382
+ } catch (NullPointerException npe) {
383
+ // TODO(uwe): printStackTrace should not fail
384
+ for (java.lang.StackTraceElement ste : t.getStackTrace()) {
385
+ output.append(ste.toString() + "\n");
386
+ }
387
+ }
388
+ }
389
+
390
+ private static String scriptsDirName(Context context) {
391
+ File storageDir = null;
392
+ if (JRubyAdapter.isDebugBuild()) {
393
+
394
+ // FIXME(uwe): Simplify this as soon as we drop support for android-7
395
+ if (android.os.Build.VERSION.SDK_INT >= 8) {
396
+ try {
397
+ Method method = context.getClass().getMethod("getExternalFilesDir", String.class);
398
+ storageDir = (File) method.invoke(context, (Object) null);
399
+ } catch (SecurityException e) {
400
+ JRubyAdapter.printStackTrace(e);
401
+ } catch (NoSuchMethodException e) {
402
+ JRubyAdapter.printStackTrace(e);
403
+ } catch (IllegalArgumentException e) {
404
+ JRubyAdapter.printStackTrace(e);
405
+ } catch (IllegalAccessException e) {
406
+ JRubyAdapter.printStackTrace(e);
407
+ } catch (InvocationTargetException e) {
408
+ JRubyAdapter.printStackTrace(e);
409
+ }
410
+ } else {
411
+ storageDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + context.getPackageName() + "/files");
412
+ Log.e("Calculated path to sdcard the old way: " + storageDir);
413
+ }
414
+ // FIXME end
415
+
416
+ if (storageDir == null || (!storageDir.exists() && !storageDir.mkdirs())) {
417
+ Log.e("Development mode active, but sdcard is not available. Make sure you have added\n<uses-permission android:name='android.permission.WRITE_EXTERNAL_STORAGE' />\nto your AndroidManifest.xml file.");
418
+ storageDir = context.getFilesDir();
419
+ }
420
+ } else {
421
+ storageDir = context.getFilesDir();
422
+ }
423
+ return storageDir.getAbsolutePath() + "/scripts";
424
+ }
425
+
426
+ private static void setDebugBuild(Context context) {
427
+ PackageManager pm = context.getPackageManager();
428
+ PackageInfo pi;
429
+ try {
430
+ pi = pm.getPackageInfo(context.getPackageName(), 0);
431
+ isDebugBuild = ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
432
+ } catch (NameNotFoundException e) {
433
+ isDebugBuild = false;
434
+ }
435
+ }
436
+
437
+ private static void setLocalContextScope(String val) {
438
+ localContextScope = val;
439
+ }
440
+
441
+ private static void setLocalVariableBehavior(String val) {
442
+ localVariableBehavior = val;
443
+ }
444
+
445
+ private static void setOutputStream(PrintStream out) {
446
+ if (ruby == null) {
447
+ output = out;
448
+ } else {
449
+ try {
450
+ Method setOutputMethod = ruby.getClass().getMethod("setOutput", PrintStream.class);
451
+ setOutputMethod.invoke(ruby, out);
452
+ Method setErrorMethod = ruby.getClass().getMethod("setError", PrintStream.class);
453
+ setErrorMethod.invoke(ruby, out);
454
+ } catch (IllegalArgumentException e) {
455
+ handleInitException(e);
456
+ } catch (SecurityException e) {
457
+ handleInitException(e);
458
+ } catch (IllegalAccessException e) {
459
+ handleInitException(e);
460
+ } catch (InvocationTargetException e) {
461
+ handleInitException(e);
462
+ } catch (NoSuchMethodException e) {
463
+ handleInitException(e);
464
+ }
465
+ }
466
+ }
467
+
468
+ }