ruboto-core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +19 -0
- data/README.md +192 -0
- data/Rakefile +144 -0
- data/assets/Rakefile +94 -0
- data/assets/assets/scripts/ruboto.rb +313 -0
- data/assets/samples/sample_activity.rb +21 -0
- data/assets/samples/sample_broadcast_receiver.rb +7 -0
- data/assets/samples/sample_service.rb +13 -0
- data/assets/src/InheritingActivity.java +10 -0
- data/assets/src/InheritingBroadcastReceiver.java +10 -0
- data/assets/src/InheritingService.java +10 -0
- data/assets/src/org/ruboto/RubotoActivity.java +1406 -0
- data/assets/src/org/ruboto/RubotoBroadcastReceiver.java +126 -0
- data/assets/src/org/ruboto/RubotoService.java +233 -0
- data/assets/src/org/ruboto/RubotoView.java +30 -0
- data/assets/src/org/ruboto/Script.java +231 -0
- data/bin/ruboto +191 -0
- data/lib/java_class_gen/InheritingClass.java.erb +11 -0
- data/lib/java_class_gen/RubotoClass.java.erb +197 -0
- data/lib/java_class_gen/callback_reflection.rb +109 -0
- data/lib/java_class_gen/interfaces.txt +1 -0
- metadata +120 -0
data/bin/ruboto
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jruby'
|
5
|
+
JRuby.objectspace = true
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'main'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'rexml/document'
|
12
|
+
require 'jruby-jars'
|
13
|
+
|
14
|
+
# fix main (to an extent)
|
15
|
+
module Main
|
16
|
+
class Program
|
17
|
+
module InstanceMethods
|
18
|
+
def setup_finalizers
|
19
|
+
@finalizers ||= []
|
20
|
+
ObjectSpace.define_finalizer(self) do
|
21
|
+
while((f = @finalizers.pop)); f.call; end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
$assets = File.expand_path(__FILE__ + "/../../assets")
|
29
|
+
|
30
|
+
class AssetCopier
|
31
|
+
def initialize(from, to)
|
32
|
+
@from = from
|
33
|
+
@to = to
|
34
|
+
end
|
35
|
+
|
36
|
+
def copy(from, to='')
|
37
|
+
FileUtils.mkdir_p(File.join(@to, to))
|
38
|
+
FileUtils.cp_r(Dir[File.join(@from, from)], File.join(@to, to))
|
39
|
+
end
|
40
|
+
|
41
|
+
def copy_from_absolute_path(from, to='')
|
42
|
+
FileUtils.mkdir_p(File.join(@to, to))
|
43
|
+
FileUtils.cp_r(Dir[from], File.join(@to, to))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_inheriting_file(klass, name, package, script_name, dest='.')
|
48
|
+
to = File.join(dest, "src/#{package.gsub('.', '/')}")
|
49
|
+
|
50
|
+
FileUtils.cp(File.expand_path(__FILE__ + "/../../assets/src/Inheriting#{klass}.java"), to)
|
51
|
+
FileUtils.move(File.join(to, "Inheriting#{klass}.java"), File.join(to, "#{name}.java"))
|
52
|
+
|
53
|
+
file = File.join(to, "#{name}.java")
|
54
|
+
text = File.read(file)
|
55
|
+
File.open(file, 'w') do |f|
|
56
|
+
f << text.gsub("THE_PACKAGE", package).gsub("Inheriting#{klass}", name).gsub("start.rb", script_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
sample_source = File.read(File.join($assets, "samples/sample_#{underscore klass}.rb"))
|
60
|
+
File.open File.join(dest, "assets/scripts/#{script_name}"), "a" do |f|
|
61
|
+
f << sample_source
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# active_support/inflector.rb
|
66
|
+
def underscore(camel_cased_word)
|
67
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
68
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
69
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
70
|
+
tr("-", "_").
|
71
|
+
downcase
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
Main {
|
77
|
+
mode "gen" do
|
78
|
+
mode "app" do
|
79
|
+
option("name"){
|
80
|
+
required
|
81
|
+
argument :required
|
82
|
+
description "Name of your app"
|
83
|
+
}
|
84
|
+
option("target") {
|
85
|
+
required
|
86
|
+
argument :required
|
87
|
+
description "android version to target. must begin with 'android-'. Currently must be 'android-8'"
|
88
|
+
}
|
89
|
+
option("path"){
|
90
|
+
required
|
91
|
+
argument :required
|
92
|
+
description "path to where you want your app."
|
93
|
+
}
|
94
|
+
option("package"){
|
95
|
+
required
|
96
|
+
argument :required
|
97
|
+
description "Name of package. Must be unique for every app. A common pattern is yourtld.yourdomain.appname (Ex. org.ruboto.irb)"
|
98
|
+
}
|
99
|
+
option("activity"){
|
100
|
+
required
|
101
|
+
argument :required
|
102
|
+
description "name of your primary Activity"
|
103
|
+
}
|
104
|
+
|
105
|
+
def run
|
106
|
+
path = params['path'].value
|
107
|
+
name = params['name'].value
|
108
|
+
target = params['target'].value
|
109
|
+
package = params['package'].value
|
110
|
+
activity = params['activity'].value
|
111
|
+
|
112
|
+
`android create project -n #{name} -t #{target} -p #{path} -k #{package} -a #{activity}`
|
113
|
+
root = File.expand_path(path)
|
114
|
+
|
115
|
+
copier = AssetCopier.new $assets, root
|
116
|
+
copier.copy "Rakefile"
|
117
|
+
copier.copy_from_absolute_path JRubyJars::core_jar_path, "libs"
|
118
|
+
copier.copy_from_absolute_path JRubyJars::stdlib_jar_path, "libs"
|
119
|
+
copier.copy ".gitignore"
|
120
|
+
copier.copy "assets"
|
121
|
+
copier.copy "src/org/ruboto/*.java", "src/org/ruboto"
|
122
|
+
|
123
|
+
java_files = File.join(root, "assets/scripts/ruboto.rb")
|
124
|
+
java_files.each do |file|
|
125
|
+
text = File.read(file)
|
126
|
+
File.open(file, 'w') do |f|
|
127
|
+
f << text.gsub("THE_PACKAGE", package).gsub("ACTIVITY_NAME", activity)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
generate_inheriting_file "Activity", activity, package, "#{underscore(activity)}.rb", path
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
mode "class" do
|
136
|
+
argument("class"){
|
137
|
+
required
|
138
|
+
description "the Android Class that you want."
|
139
|
+
}
|
140
|
+
|
141
|
+
option("script_name"){
|
142
|
+
argument :required
|
143
|
+
description "name of the ruby script in assets/scripts/ that this class will execute. should end in .rb. optional"
|
144
|
+
}
|
145
|
+
|
146
|
+
option("name"){
|
147
|
+
required
|
148
|
+
argument :required
|
149
|
+
description "name of the class (and file). Should be CamelCase"
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
def run
|
154
|
+
package = REXML::Document.new(File.read('AndroidManifest.xml')).root.attribute('package').value
|
155
|
+
name = params['name'].value
|
156
|
+
script_name = params['script_name'].value || "#{underscore(name)}.rb"
|
157
|
+
klass = params['class'].value
|
158
|
+
|
159
|
+
generate_inheriting_file klass, name, package, script_name
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
mode "key" do
|
164
|
+
option("keystore"){
|
165
|
+
default "~/.android/production.keystore"
|
166
|
+
description "path to where the keystore will be saved. defaults to ~/.android/production.keystore"
|
167
|
+
}
|
168
|
+
|
169
|
+
option("alias"){
|
170
|
+
required
|
171
|
+
description "The 'alias' for the key. Identifies the key within the keystore. Required"
|
172
|
+
}
|
173
|
+
|
174
|
+
def run
|
175
|
+
keystore = params['keystore'].value
|
176
|
+
key_alias = params['alias'].value
|
177
|
+
|
178
|
+
`keytool -genkey -keyalg rsa -keysize 4096 -validity 1000000 -keystore #{keystore} -alias #{key_alias}`
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# just running `ruboto`
|
184
|
+
def run
|
185
|
+
puts %Q{
|
186
|
+
Ruboto -- Ruby for Android
|
187
|
+
Execute `ruboto gen app --help` for instructions on how to generate a fresh Ruby-enabled Android app
|
188
|
+
}
|
189
|
+
end
|
190
|
+
}
|
191
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
package THE_PACKAGE;
|
2
|
+
|
3
|
+
public class Inheriting<%= @class %> extends org.ruboto.Ruboto<%= @class %> {
|
4
|
+
% v = @callbacks[@full_class][@first_method]
|
5
|
+
public <%= "#{v["return_type"]} #{v["interface_method"]}(#{v["args_with_types"] })" %> {
|
6
|
+
|
7
|
+
setScriptName("start.rb");
|
8
|
+
super.<%= @first_method %>(<%= v["args_alone"].join(',') %>);
|
9
|
+
}
|
10
|
+
|
11
|
+
}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
/**********************************************************************************************
|
2
|
+
*
|
3
|
+
* Ruboto<%= @class %>.java is generated from RubotoClass.java.erb. Any changes needed in should be
|
4
|
+
* made within the erb template or they will be lost.
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
|
8
|
+
package org.ruboto;
|
9
|
+
|
10
|
+
import java.io.IOException;
|
11
|
+
import <%= @full_class %>;
|
12
|
+
% if @class == "Activity"
|
13
|
+
import android.app.ProgressDialog;
|
14
|
+
% end
|
15
|
+
import android.os.Handler;
|
16
|
+
import android.os.Bundle;
|
17
|
+
|
18
|
+
import org.jruby.Ruby;
|
19
|
+
import org.jruby.javasupport.util.RuntimeHelpers;
|
20
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
21
|
+
import org.jruby.javasupport.JavaUtil;
|
22
|
+
import org.jruby.exceptions.RaiseException;
|
23
|
+
|
24
|
+
public abstract class Ruboto<%= @class %> extends <%= @class %>
|
25
|
+
%##############################################################################################
|
26
|
+
%#
|
27
|
+
%# Implement all interfaces
|
28
|
+
%#
|
29
|
+
<%= "implements\n #{@implements.join(",\n ")}" unless @implements.empty? %>
|
30
|
+
%##############################################################################################
|
31
|
+
{
|
32
|
+
%##############################################################################################
|
33
|
+
%#
|
34
|
+
%# Create constants for all callbacks (may get rid of this means of requesting callbacks later)
|
35
|
+
%#
|
36
|
+
% @constants.each_with_index do |c, i|
|
37
|
+
<%= " public static final int #{c} = #{i};" %>
|
38
|
+
% end
|
39
|
+
%##############################################################################################
|
40
|
+
public static final int CB_LAST = <%= @constants.size %>;
|
41
|
+
|
42
|
+
private boolean[] callbackOptions = new boolean [CB_LAST];
|
43
|
+
|
44
|
+
private String remoteVariable = "";
|
45
|
+
|
46
|
+
private final Handler loadingHandler = new Handler();
|
47
|
+
private IRubyObject __this__;
|
48
|
+
private Ruby __ruby__;
|
49
|
+
private String scriptName;
|
50
|
+
public Object[] args;
|
51
|
+
|
52
|
+
public Ruboto<%= @class %> setRemoteVariable(String var) {
|
53
|
+
remoteVariable = ((var == null) ? "" : (var + "."));
|
54
|
+
return this;
|
55
|
+
}
|
56
|
+
|
57
|
+
/**********************************************************************************
|
58
|
+
*
|
59
|
+
* Callback management
|
60
|
+
*/
|
61
|
+
|
62
|
+
public void requestCallback(int id) {
|
63
|
+
callbackOptions[id] = true;
|
64
|
+
}
|
65
|
+
|
66
|
+
public void removeCallback(int id) {
|
67
|
+
callbackOptions[id] = false;
|
68
|
+
}
|
69
|
+
|
70
|
+
/*
|
71
|
+
* Activity Lifecycle: onCreate
|
72
|
+
*/
|
73
|
+
|
74
|
+
@Override
|
75
|
+
% v = @callbacks[@full_class].delete(@first_method)
|
76
|
+
public <%= "#{v["return_type"]} #{v["interface_method"]}(#{v["args_with_types"] })" %> {
|
77
|
+
|
78
|
+
args = new Object[<%= v["args_alone"].length %>];
|
79
|
+
% v["args_alone"].each_with_index do |arg, index|
|
80
|
+
args[<%= index %>] = <%= arg %>;
|
81
|
+
% end
|
82
|
+
% unless v["abstract"]
|
83
|
+
super.<%= @first_method %>(<%= v["args_alone"].join(',') %>);
|
84
|
+
% end
|
85
|
+
|
86
|
+
% if @class == "Activity"
|
87
|
+
% # we'll assume the first thing that happens is an activity gets spawned. no need to do this in other classes
|
88
|
+
Script.copyScriptsIfNeeded(getFilesDir().getAbsolutePath() + "/scripts", getAssets());
|
89
|
+
% end
|
90
|
+
|
91
|
+
if (Script.getRuby() == null){
|
92
|
+
Script.setUpJRuby(null);
|
93
|
+
}
|
94
|
+
Script.defineGlobalVariable("$<%= underscore(@class) %>", this);
|
95
|
+
|
96
|
+
|
97
|
+
__ruby__ = Script.getRuby();
|
98
|
+
__this__ = JavaUtil.convertJavaToRuby(__ruby__, Ruboto<%= @class %>.this);
|
99
|
+
|
100
|
+
% if @class == "Activity"
|
101
|
+
Bundle configBundle = getIntent().getBundleExtra("RubotoActivity Config");
|
102
|
+
|
103
|
+
Script.defineGlobalVariable("$bundle", arg0);
|
104
|
+
|
105
|
+
if (configBundle != null) {
|
106
|
+
setRemoteVariable(configBundle.getString("Remote Variable"));
|
107
|
+
if (configBundle.getBoolean("Define Remote Variable")) {
|
108
|
+
Script.defineGlobalVariable(configBundle.getString("Remote Variable"), this);
|
109
|
+
setRemoteVariable(configBundle.getString("Remote Variable"));
|
110
|
+
}
|
111
|
+
if (configBundle.getString("Initialize Script") != null) {
|
112
|
+
Script.execute(configBundle.getString("Initialize Script"));
|
113
|
+
}
|
114
|
+
Script.execute(remoteVariable + "on_create($bundle)");
|
115
|
+
} else {
|
116
|
+
Script.defineGlobalVariable("$activity", this);
|
117
|
+
|
118
|
+
try {
|
119
|
+
new Script(scriptName).execute();
|
120
|
+
}
|
121
|
+
catch(IOException e){
|
122
|
+
ProgressDialog.show(this, "Script failed", "Something bad happened", true, false);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
% else
|
126
|
+
try {
|
127
|
+
new Script(scriptName).execute();
|
128
|
+
}
|
129
|
+
catch(IOException e){
|
130
|
+
|
131
|
+
}
|
132
|
+
% end
|
133
|
+
}
|
134
|
+
|
135
|
+
public void setScriptName(String name){
|
136
|
+
scriptName = name;
|
137
|
+
}
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
/*********************************************************************************
|
142
|
+
*
|
143
|
+
* Ruby Generated Callback Methods
|
144
|
+
*/
|
145
|
+
|
146
|
+
%##############################################################################################
|
147
|
+
%#
|
148
|
+
%# Create one Java callback methods
|
149
|
+
%#
|
150
|
+
% @callbacks.each do |interface,i_info|
|
151
|
+
/*
|
152
|
+
* <%= interface %>
|
153
|
+
*/
|
154
|
+
|
155
|
+
% i_info.each do |method,v|
|
156
|
+
public <%= "#{v["return_type"]} #{v["interface_method"]}(#{v["args_with_types"] })" %> {
|
157
|
+
if (callbackOptions[<%= v['constant']%>]) {
|
158
|
+
<%= "super.#{v["method"]}(#{v["args_alone"].join(", ")});" if v["interface"] == "Activity" %>
|
159
|
+
try {
|
160
|
+
% if v["args_alone"].size > 3
|
161
|
+
IRubyObject[] args = {<%= v["args_alone"].map{|i| "JavaUtil.convertJavaToRuby(__ruby__, #{i})"}.join(", ") %>};
|
162
|
+
<%= "return (#{transform_return_type v["return_type"]})" if v["return_type"] != "void" %>RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "<%=v["ruby_method"]%>", args)<%= ".toJava(#{v["return_type"]}.class)" if v["return_type"] != "void" %>;
|
163
|
+
% elsif v["args_alone"].size > 0
|
164
|
+
<%= "return (#{transform_return_type v["return_type"]})" if v["return_type"] != "void" %>RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "<%=v["ruby_method"]%>", <%= v["args_alone"].map{|i| "JavaUtil.convertJavaToRuby(__ruby__, #{i})"}.join(", ") %>)<%= ".toJava(#{v["return_type"]}.class)" if v["return_type"] != "void"%>;
|
165
|
+
% else
|
166
|
+
<%= "return (#{transform_return_type v["return_type"]})" if v["return_type"] != "void" %>RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "<%=v["ruby_method"]%>")<%= ".toJava(#{v["return_type"]}.class)" if v["return_type"] != "void"%>;
|
167
|
+
% end
|
168
|
+
} catch (RaiseException re) {
|
169
|
+
re.printStackTrace(__ruby__.getErrorStream());
|
170
|
+
<%= case v["return_type"]
|
171
|
+
when "boolean": "return false;"
|
172
|
+
when "int": "return 0;"
|
173
|
+
when "void": ""
|
174
|
+
else "return null;"
|
175
|
+
end
|
176
|
+
%>
|
177
|
+
}
|
178
|
+
% if v["interface"] == "Activity"
|
179
|
+
} else {
|
180
|
+
<%= "return " unless v["return_type"] == "void" %><%= "super.#{v["method"]}(#{v["args_alone"].join(", ")});" %>
|
181
|
+
% elsif v["return_type"] != "void"
|
182
|
+
} else {
|
183
|
+
<%= case v["return_type"]
|
184
|
+
when "boolean": "return false;"
|
185
|
+
when "int": "return 0;"
|
186
|
+
when "void": ""
|
187
|
+
else "return null;"
|
188
|
+
end
|
189
|
+
%>
|
190
|
+
% end
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
% end
|
195
|
+
% end
|
196
|
+
%##############################################################################################
|
197
|
+
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#
|
2
|
+
# Run on a device and then copy interfaces.txt back to the callback_gen directory.
|
3
|
+
#
|
4
|
+
# The result is a hash.
|
5
|
+
# The keys are names the names of the classes, sometimes followed by a
|
6
|
+
# $ and then the name of the interface.
|
7
|
+
# The keys are hashes themselves. These hashes have keys of the method
|
8
|
+
# names and values of yet another hash, which gives the argument types
|
9
|
+
# (and thus implicitly the number of args), the return type, etc.
|
10
|
+
|
11
|
+
require 'java'
|
12
|
+
|
13
|
+
class ReflectionBuilder
|
14
|
+
attr_reader :methods
|
15
|
+
|
16
|
+
def initialize(class_name, callbacks_only=false)
|
17
|
+
@methods = {}
|
18
|
+
@@count = 0 unless defined?(@@count)
|
19
|
+
reflect class_name, callbacks_only
|
20
|
+
end
|
21
|
+
|
22
|
+
def reflect(klass, callbacks_only=false)
|
23
|
+
# klass can be the java class object or a string
|
24
|
+
klass = java_class klass if klass.class == String
|
25
|
+
|
26
|
+
hash = @methods[klass.getName] = {}
|
27
|
+
|
28
|
+
# iterate over the public methods of the class
|
29
|
+
klass.getDeclaredMethods.reject {|method| java.lang.reflect.Modifier.isPrivate(method.getModifiers) || java.lang.reflect.Modifier.isFinal(method.getModifiers) }.each do |method|
|
30
|
+
if !callbacks_only or method.getName[0..1] == "on"
|
31
|
+
hash[method.getName] = {}
|
32
|
+
hash[method.getName]["return_type"] = method.getReturnType.getName unless method.getReturnType.getName == "void"
|
33
|
+
# the gsub below converts [Ljava nonsense to ClassName[]
|
34
|
+
hash[method.getName]["args"] = method.getParameterTypes.map {|i| i.getName.gsub(/\[Ljava\.[\w\.]+;/) {|m| "#{m[2..-2]}[]"}} unless method.getParameterTypes.empty?
|
35
|
+
hash[method.getName]["abstract"] = java.lang.reflect.Modifier.isAbstract(method.getModifiers)
|
36
|
+
@@count += 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def interfaces(*args)
|
42
|
+
args.each {|arg| reflect arg}
|
43
|
+
end
|
44
|
+
|
45
|
+
def interfaces_under(*args)
|
46
|
+
args.each do |name|
|
47
|
+
interfaces *java.lang.Class.forName(name).getClasses.select {|klass| klass.isInterface}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.count
|
52
|
+
@@count
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.reset_count
|
56
|
+
@@count = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def java_class(class_name)
|
61
|
+
java.lang.Class.forName(class_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def java_reflect(class_name, callbacks_only=false, &block)
|
66
|
+
r = ReflectionBuilder.new(class_name, callbacks_only)
|
67
|
+
yield r if block
|
68
|
+
$result[class_name] = r.methods
|
69
|
+
end
|
70
|
+
|
71
|
+
def reflect_multiple(*classes)
|
72
|
+
classes.each {|c| java_reflect c }
|
73
|
+
end
|
74
|
+
|
75
|
+
$result = {}
|
76
|
+
ReflectionBuilder.reset_count
|
77
|
+
|
78
|
+
java_reflect 'android.app.Activity', true do |r|
|
79
|
+
r.interfaces *%w(
|
80
|
+
android.hardware.SensorEventListener
|
81
|
+
java.lang.Runnable
|
82
|
+
)
|
83
|
+
|
84
|
+
r.interfaces_under *%w(
|
85
|
+
android.view.View
|
86
|
+
android.widget.AdapterView
|
87
|
+
android.widget.TabHost
|
88
|
+
android.widget.TextView
|
89
|
+
android.widget.DatePicker
|
90
|
+
android.widget.TimePicker
|
91
|
+
android.app.DatePickerDialog
|
92
|
+
android.app.TimePickerDialog
|
93
|
+
android.content.DialogInterface
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
reflect_multiple *%w(
|
98
|
+
android.app.Service
|
99
|
+
android.content.BroadcastReceiver
|
100
|
+
)
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
File.open("/sdcard/jruby/interfaces.txt", "w") do |file|
|
105
|
+
file.write $result.inspect
|
106
|
+
end
|
107
|
+
|
108
|
+
puts ReflectionBuilder.count
|
109
|
+
|