ruboto-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+