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.
- 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
|
+
}
|