ruboto 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -49
- data/Rakefile +26 -26
- data/assets/rakelib/ruboto.rake +29 -9
- data/assets/res/drawable-hdpi/{icon.png → ic_launcher.png} +0 -0
- data/assets/res/drawable-ldpi/{icon.png → ic_launcher.png} +0 -0
- data/assets/res/drawable-mdpi/{icon.png → ic_launcher.png} +0 -0
- data/assets/samples/sample_activity.rb +19 -9
- data/assets/samples/sample_broadcast_receiver.rb +3 -1
- data/assets/samples/sample_service.rb +9 -9
- data/assets/src/InheritingActivity.java +1 -1
- data/assets/src/InheritingBroadcastReceiver.java +4 -4
- data/assets/src/InheritingClass.java +1 -1
- data/assets/src/InheritingService.java +2 -1
- data/assets/src/RubotoActivity.java +26 -17
- data/assets/src/RubotoBroadcastReceiver.java +32 -11
- data/assets/src/RubotoService.java +23 -13
- data/assets/src/org/ruboto/EntryPointActivity.java +18 -26
- data/assets/src/org/ruboto/JRubyAdapter.java +468 -0
- data/assets/src/org/ruboto/Log.java +22 -0
- data/assets/src/org/ruboto/Script.java +113 -587
- data/assets/src/org/ruboto/test/ActivityTest.java +7 -7
- data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +27 -12
- data/assets/src/ruboto.rb +1 -1
- data/assets/src/ruboto/activity.rb +10 -1
- data/assets/src/ruboto/base.rb +0 -12
- data/assets/src/ruboto/broadcast_receiver.rb +12 -0
- data/assets/src/ruboto/menu.rb +0 -1
- data/assets/src/ruboto/package.rb +11 -0
- data/assets/src/ruboto/service.rb +9 -0
- data/lib/ruboto/commands/base.rb +1 -1
- data/lib/ruboto/util/build.rb +1 -1
- data/lib/ruboto/util/update.rb +35 -27
- data/lib/ruboto/util/xml_element.rb +25 -12
- data/lib/ruboto/version.rb +2 -2
- data/test/activity/image_button_activity.rb +14 -11
- data/test/activity/image_button_activity_test.rb +2 -6
- data/test/activity/image_button_and_button_activity.rb +15 -17
- data/test/activity/image_button_and_button_activity_test.rb +4 -8
- data/test/activity/option_menu_activity.rb +17 -12
- data/test/activity/option_menu_activity_test.rb +1 -4
- data/test/activity/psych_activity.rb +20 -13
- data/test/activity/psych_activity_test.rb +3 -1
- data/test/activity/stack_activity.rb +17 -14
- data/test/activity/stack_activity_test.rb +13 -12
- data/test/app_test_methods.rb +25 -19
- data/test/block_def_activity/image_button_activity.rb +23 -0
- data/test/block_def_activity/image_button_activity_test.rb +21 -0
- data/test/block_def_activity/image_button_and_button_activity.rb +20 -0
- data/test/block_def_activity/image_button_and_button_activity_test.rb +27 -0
- data/test/block_def_activity/option_menu_activity.rb +26 -0
- data/test/block_def_activity/option_menu_activity_test.rb +18 -0
- data/test/block_def_activity/psych_activity.rb +35 -0
- data/test/block_def_activity/psych_activity_test.rb +16 -0
- data/test/block_def_activity/stack_activity.rb +25 -0
- data/test/block_def_activity/stack_activity_test.rb +31 -0
- data/test/broadcast_receiver_test.rb +2 -2
- data/test/handle_activity/image_button_activity.rb +21 -0
- data/test/handle_activity/image_button_activity_test.rb +21 -0
- data/test/handle_activity/image_button_and_button_activity.rb +24 -0
- data/test/handle_activity/image_button_and_button_activity_test.rb +27 -0
- data/test/handle_activity/option_menu_activity.rb +21 -0
- data/test/handle_activity/option_menu_activity_test.rb +20 -0
- data/test/handle_activity/psych_activity.rb +31 -0
- data/test/handle_activity/psych_activity_test.rb +16 -0
- data/test/handle_activity/stack_activity.rb +21 -0
- data/test/handle_activity/stack_activity_test.rb +32 -0
- data/test/minimal_app_test.rb +4 -4
- data/test/rake_test.rb +15 -1
- data/test/ruboto_gen_test.rb +7 -4
- data/test/service_test.rb +110 -21
- data/test/test_helper.rb +17 -14
- data/test/updated_example_test_methods.rb +5 -14
- 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
|
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 (
|
28
|
+
if (JRubyAdapter.isInitialized()) {
|
29
29
|
loadScript();
|
30
30
|
}
|
31
31
|
}
|
32
32
|
}
|
33
33
|
|
34
34
|
protected void loadScript() {
|
35
|
-
|
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
|
-
|
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
|
-
|
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 (
|
41
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
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("
|
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 (
|
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("
|
39
|
+
Log.d("onResume: ");
|
47
40
|
|
48
41
|
if(appStarted) {
|
49
|
-
Log.d("
|
42
|
+
Log.d("onResume: App already started!");
|
50
43
|
super.onResume();
|
51
44
|
return;
|
52
45
|
}
|
53
46
|
|
54
|
-
Log.d("
|
55
|
-
if (
|
56
|
-
Log.d("
|
47
|
+
Log.d("onResume: Checking JRuby");
|
48
|
+
if (JRubyAdapter.isInitialized()) {
|
49
|
+
Log.d("Already initialized");
|
57
50
|
fireRubotoActivity();
|
58
51
|
} else {
|
59
|
-
Log.d("
|
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("
|
64
|
-
Log.i("
|
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("
|
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("
|
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 =
|
100
|
+
final boolean jrubyOk = JRubyAdapter.setUpJRuby(EntryPointActivity.this);
|
108
101
|
if (jrubyOk) {
|
109
|
-
Log.d("
|
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("
|
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("
|
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("
|
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("
|
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
|
+
}
|