ruboto-core 0.0.1
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/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
|
+
|