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/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
+