-
-
Save rscottm/438284 to your computer and use it in GitHub Desktop.
result = {} | |
@count = 0 | |
def hash_methods(klass, include_all=true) | |
rv = {} | |
klass.getDeclaredMethods.each do |method| | |
if include_all or method.getName[0..1] == "on" | |
rv[method.getName] = {} | |
rv[method.getName]["return_type"] = method.getReturnType.getName unless method.getReturnType.getName == "void" | |
rv[method.getName]["args"] = method.getParameterTypes.map{|i| i.getName} unless method.getParameterTypes.empty? | |
@count += 1 | |
end | |
end | |
rv | |
end | |
%w( | |
android.app.Activity | |
).each do |name| | |
h = hash_methods(java.lang.Class.forName(name), false) | |
result[name] = h unless h.empty? | |
end | |
%w( | |
android.hardware.SensorEventListener | |
java.lang.Runnable | |
).each do |name| | |
h = hash_methods(java.lang.Class.forName(name)) | |
result[name] = h unless h.empty? | |
end | |
%w( | |
android.view.View | |
android.widget.AdapterView | |
android.widget.TabHost | |
android.widget.TextView | |
android.widget.DatePicker | |
android.widget.TimePicker | |
android.app.DatePickerDialog | |
android.app.TimePickerDialog | |
android.content.DialogInterface | |
).each do |name| | |
java.lang.Class.forName(name).getClasses.each do |klass| | |
if klass.isInterface | |
h = hash_methods(klass) | |
result[klass.getName] = h unless h.empty? | |
end | |
end | |
end | |
File.open("/sdcard/jruby/interfaces.txt", "w") do |file| | |
file.write result.inspect | |
end | |
puts @count |
# | |
# Note: callback_reflection.rb creates a interfaces.txt file because JRuby can't do YAML with ruby 1.8. | |
# The txt file is just and inspect on the hash. After brining it off the phone I read it in, do an eval, | |
# and then write it out as interfaces.yml. This will only be necessary if we want the ability to | |
# edit the yaml file by hand. If not, then this code could just use the txt file instead. | |
# | |
# I imagine this code will get move into a rake task. | |
# | |
require 'erb' | |
require 'yaml' | |
@callbacks = YAML.load_file "interfaces.yml" | |
File.open("../src/org/jruby/ruboto/RubotoActivity.java", "w") do |file| | |
file.write ERB.new(IO.read("RubotoActivity.erb"), 0, "%").result | |
end |
--- | |
android.content.DialogInterface$OnCancelListener: | |
onCancel: | |
args: | |
- android.content.DialogInterface | |
android.widget.TabHost$TabContentFactory: | |
createTabContent: | |
args: | |
- java.lang.String | |
return_type: android.view.View | |
android.content.DialogInterface$OnShowListener: | |
onShow: | |
args: | |
- android.content.DialogInterface | |
android.widget.TimePicker$OnTimeChangedListener: | |
onTimeChanged: | |
args: | |
- android.widget.TimePicker | |
- int | |
- int | |
android.widget.AdapterView$OnItemLongClickListener: | |
onItemLongClick: | |
args: | |
- android.widget.AdapterView | |
- android.view.View | |
- int | |
- long | |
return_type: boolean | |
android.view.View$OnClickListener: | |
onClick: | |
args: | |
- android.view.View | |
android.content.DialogInterface$OnDismissListener: | |
onDismiss: | |
args: | |
- android.content.DialogInterface | |
android.content.DialogInterface$OnClickListener: | |
onClick: | |
args: | |
- android.content.DialogInterface | |
- int | |
android.content.DialogInterface$OnKeyListener: | |
onKey: | |
args: | |
- android.content.DialogInterface | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
android.view.View$OnTouchListener: | |
onTouch: | |
args: | |
- android.view.View | |
- android.view.MotionEvent | |
return_type: boolean | |
android.app.DatePickerDialog$OnDateSetListener: | |
onDateSet: | |
args: | |
- android.widget.DatePicker | |
- int | |
- int | |
- int | |
android.widget.AdapterView$OnItemClickListener: | |
onItemClick: | |
args: | |
- android.widget.AdapterView | |
- android.view.View | |
- int | |
- long | |
android.hardware.SensorEventListener: | |
onSensorChanged: | |
args: | |
- android.hardware.SensorEvent | |
onAccuracyChanged: | |
args: | |
- android.hardware.Sensor | |
- int | |
android.view.View$OnLongClickListener: | |
onLongClick: | |
args: | |
- android.view.View | |
return_type: boolean | |
android.widget.TextView$OnEditorActionListener: | |
onEditorAction: | |
args: | |
- android.widget.TextView | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
android.view.View$OnCreateContextMenuListener: | |
onCreateContextMenu: | |
args: | |
- android.view.ContextMenu | |
- android.view.View | |
- android.view.ContextMenu$ContextMenuInfo | |
android.content.DialogInterface$OnMultiChoiceClickListener: | |
onClick: | |
args: | |
- android.content.DialogInterface | |
- int | |
- boolean | |
android.widget.DatePicker$OnDateChangedListener: | |
onDateChanged: | |
args: | |
- android.widget.DatePicker | |
- int | |
- int | |
- int | |
android.view.ViewGroup$OnHierarchyChangeListener: | |
onChildViewRemoved: | |
args: | |
- android.view.View | |
- android.view.View | |
onChildViewAdded: | |
args: | |
- android.view.View | |
- android.view.View | |
android.widget.AdapterView$OnItemSelectedListener: | |
onNothingSelected: | |
args: | |
- android.widget.AdapterView | |
onItemSelected: | |
args: | |
- android.widget.AdapterView | |
- android.view.View | |
- int | |
- long | |
android.view.View$OnFocusChangeListener: | |
onFocusChange: | |
args: | |
- android.view.View | |
- boolean | |
java.lang.Runnable: | |
run: {} | |
android.view.View$OnKeyListener: | |
onKey: | |
args: | |
- android.view.View | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
android.app.Activity: | |
onSaveInstanceState: | |
args: | |
- android.os.Bundle | |
onLowMemory: {} | |
onUserInteraction: {} | |
onStop: {} | |
onPostResume: {} | |
onCreatePanelMenu: | |
args: | |
- int | |
- android.view.Menu | |
return_type: boolean | |
onWindowFocusChanged: | |
args: | |
- boolean | |
onUserLeaveHint: {} | |
onDetachedFromWindow: {} | |
onCreateView: | |
args: | |
- java.lang.String | |
- android.content.Context | |
- android.util.AttributeSet | |
return_type: android.view.View | |
onPrepareOptionsMenu: | |
args: | |
- android.view.Menu | |
return_type: boolean | |
onPause: {} | |
onOptionsMenuClosed: | |
args: | |
- android.view.Menu | |
onMenuItemSelected: | |
args: | |
- int | |
- android.view.MenuItem | |
return_type: boolean | |
onCreateDescription: | |
return_type: java.lang.CharSequence | |
onRetainNonConfigurationInstance: | |
return_type: java.lang.Object | |
onRestoreInstanceState: | |
args: | |
- android.os.Bundle | |
onOptionsItemSelected: | |
args: | |
- android.view.MenuItem | |
return_type: boolean | |
onActivityResult: | |
args: | |
- int | |
- int | |
- android.content.Intent | |
onStart: {} | |
onResume: {} | |
onDestroy: {} | |
onCreatePanelView: | |
args: | |
- int | |
return_type: android.view.View | |
onCreateDialog: | |
args: | |
- int | |
- android.os.Bundle | |
return_type: android.app.Dialog | |
onRetainNonConfigurationChildInstances: | |
return_type: java.util.HashMap | |
onKeyDown: | |
args: | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
onCreateOptionsMenu: | |
args: | |
- android.view.Menu | |
return_type: boolean | |
onContentChanged: {} | |
onWindowAttributesChanged: | |
args: | |
- android.view.WindowManager$LayoutParams | |
onTitleChanged: | |
args: | |
- java.lang.CharSequence | |
- int | |
onSearchRequested: | |
return_type: boolean | |
onRestart: {} | |
onPrepareDialog: | |
args: | |
- int | |
- android.app.Dialog | |
- android.os.Bundle | |
onPostCreate: | |
args: | |
- android.os.Bundle | |
onKeyMultiple: | |
args: | |
- int | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
onKeyLongPress: | |
args: | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
onCreate: | |
args: | |
- android.os.Bundle | |
onContextMenuClosed: | |
args: | |
- android.view.Menu | |
onConfigurationChanged: | |
args: | |
- android.content.res.Configuration | |
onChildTitleChanged: | |
args: | |
- android.app.Activity | |
- java.lang.CharSequence | |
onAttachedToWindow: {} | |
onTrackballEvent: | |
args: | |
- android.view.MotionEvent | |
return_type: boolean | |
onTouchEvent: | |
args: | |
- android.view.MotionEvent | |
return_type: boolean | |
onPreparePanel: | |
args: | |
- int | |
- android.view.View | |
- android.view.Menu | |
return_type: boolean | |
onCreateThumbnail: | |
args: | |
- android.graphics.Bitmap | |
- android.graphics.Canvas | |
return_type: boolean | |
onCreateContextMenu: | |
args: | |
- android.view.ContextMenu | |
- android.view.View | |
- android.view.ContextMenu$ContextMenuInfo | |
onBackPressed: {} | |
onApplyThemeResource: | |
args: | |
- android.content.res.Resources$Theme | |
- int | |
- boolean | |
onNewIntent: | |
args: | |
- android.content.Intent | |
onMenuOpened: | |
args: | |
- int | |
- android.view.Menu | |
return_type: boolean | |
onKeyUp: | |
args: | |
- int | |
- android.view.KeyEvent | |
return_type: boolean | |
onPanelClosed: | |
args: | |
- int | |
- android.view.Menu | |
onContextItemSelected: | |
args: | |
- android.view.MenuItem | |
return_type: boolean | |
android.app.TimePickerDialog$OnTimeSetListener: | |
onTimeSet: | |
args: | |
- android.widget.TimePicker | |
- int | |
- int | |
android.widget.TabHost$OnTabChangeListener: | |
onTabChanged: | |
args: | |
- java.lang.String |
%############################################################################################## | |
%# | |
%# This code takes the @callbacks hash (read out of the interfaces.yml file) and prepares | |
%# it for use in the code below. | |
%# | |
% implements = [] | |
% constants = [] | |
% @callbacks.each do |interface,i_info| | |
% i_info.each do |method,v| | |
% v["interface"] = interface.gsub("$", ".") | |
% v["interface"] = "Activity" if v["interface"] == "android.app.Activity" | |
% v["method"] = method | |
% v["return_type"] = (v["return_type"] || "void").gsub("$", ".") | |
% v["interface_method"] = v["interface_method"] || v["method"] | |
% v["ruby_method"] = v["method"].gsub(/[A-Z]/) {|i| "_#{i.downcase}"} | |
% | |
% implements << v["interface"] if v["interface"] != "Activity" and | |
% v["interface"] != "none" and | |
% not implements.include?(v["interface"]) | |
% | |
% unless v["constant"] | |
% constant = v["method"].gsub(/[A-Z]/) {|i| "_#{i}"}.upcase | |
% constant = constant[3..-1] if constant[0..2] == "ON_" | |
% v["constant"] = "CB_#{constant}" | |
% end | |
% constants << v["constant"] unless constants.include?(v["constant"]) | |
% | |
% v["args"] = (v["args"] || []) | |
% v["args_with_types"], v["args_alone"] = [], [] | |
% v["args"].each_with_index {|arg_type, i| v["args_with_types"] << "#{arg_type.gsub("$", ".")} arg#{i}"; v["args_alone"] << "arg#{i}"} | |
% v["args_with_types"] = v["args_with_types"].join(", ") | |
% end | |
% end | |
%############################################################################################## | |
/********************************************************************************************** | |
* | |
* RubotoActivity.java is generated from RubotoActivity.erb. Any changes needed in should be | |
* made within the erb template or they will be lost. | |
* | |
*/ | |
package org.jruby.ruboto; | |
import java.io.IOException; | |
import android.app.Activity; | |
import android.app.ProgressDialog; | |
import android.os.Handler; | |
import android.os.Bundle; | |
import org.jruby.Ruby; | |
import org.jruby.javasupport.util.RuntimeHelpers; | |
import org.jruby.runtime.builtin.IRubyObject; | |
import org.jruby.javasupport.JavaUtil; | |
import org.jruby.exceptions.RaiseException; | |
public class RubotoActivity extends Activity | |
%############################################################################################## | |
%# | |
%# Implement all interfaces | |
%# | |
<%= "implements\n #{implements.join(",\n ")}" unless implements.empty? %> | |
%############################################################################################## | |
{ | |
%############################################################################################## | |
%# | |
%# Create constants for all callbacks (may get rid of this means of requesting callbacks later) | |
%# | |
% constants.each_with_index do |c, i| | |
<%= " public static final int #{c} = #{i};" %> | |
% end | |
%############################################################################################## | |
public static final int CB_LAST = <%= constants.size %>; | |
private boolean[] callbackOptions = new boolean [CB_LAST]; | |
private String remoteVariable = ""; | |
private ProgressDialog loadingDialog; | |
private final Handler loadingHandler = new Handler(); | |
private IRubyObject __this__; | |
private Ruby __ruby__; | |
public RubotoActivity setRemoteVariable(String var) { | |
remoteVariable = ((var == null) ? "" : (var + ".")); | |
return this; | |
} | |
/********************************************************************************** | |
* | |
* Callback management | |
*/ | |
public void requestCallback(int id) { | |
callbackOptions[id] = true; | |
} | |
public void removeCallback(int id) { | |
callbackOptions[id] = false; | |
} | |
/* | |
* Activity Lifecycle: onCreate | |
*/ | |
@Override | |
public void onCreate(Bundle savedState) { | |
super.onCreate(savedState); | |
if (getIntent().getAction() != null && | |
getIntent().getAction().equals("org.ruboto.intent.action.LAUNCH_SCRIPT")) { | |
/* Launched from a shortcut */ | |
Thread t = new Thread() { | |
public void run(){ | |
Script.configDir(IRB.SDCARD_SCRIPTS_DIR, getFilesDir().getAbsolutePath() + "/scripts"); | |
Script.setUpJRuby(null); | |
loadingHandler.post(loadingComplete); | |
} | |
}; | |
t.start(); | |
loadingDialog = ProgressDialog.show(this, "Launching Script", "Initializing Ruboto...", true, false); | |
} else { | |
Bundle configBundle = getIntent().getBundleExtra("RubotoActivity Config"); | |
if (configBundle != null) { | |
setRemoteVariable(configBundle.getString("Remote Variable")); | |
if (configBundle.getBoolean("Define Remote Variable")) { | |
Script.defineGlobalVariable(configBundle.getString("Remote Variable"), this); | |
} | |
if (configBundle.getString("Initialize Script") != null) { | |
Script.execute(configBundle.getString("Initialize Script")); | |
} | |
} else { | |
Script.defineGlobalVariable("$activity", this); | |
} | |
__ruby__ = Script.getRuby(); | |
__this__ = JavaUtil.convertJavaToRuby(__ruby__, this); | |
Script.defineGlobalVariable("$bundle", savedState); | |
Script.execute(remoteVariable + "on_create($bundle)"); | |
// RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "on_create", JavaUtil.convertJavaToRuby(__ruby__, savedState)); | |
} | |
} | |
protected final Runnable loadingComplete = new Runnable(){ | |
public void run(){ | |
loadingDialog.dismiss(); | |
__ruby__ = Script.getRuby(); | |
__this__ = JavaUtil.convertJavaToRuby(__ruby__, RubotoActivity.this); | |
Script script = new Script(getIntent().getExtras().getString("org.ruboto.extra.SCRIPT_NAME")); | |
Script.defineGlobalVariable("$activity", RubotoActivity.this); | |
try {script.execute();} | |
catch (IOException e) {finish();} | |
} | |
}; | |
/********************************************************************************* | |
* | |
* Ruby Generated Callback Methods | |
*/ | |
%############################################################################################## | |
%# | |
%# Create one Java callback methods | |
%# | |
% @callbacks.each do |interface,i_info| | |
% i_info.each do |method,v| | |
public <%= "#{v["return_type"]} #{v["interface_method"]}(#{v["args_with_types"]})" %> { | |
<%= "super.#{v["method"]}(#{v["args_alone"].join(", ")});" if v["interface"] == "Activity" %> | |
if (callbackOptions[<%= v['constant']%>]) { | |
try { | |
% if v["args_alone"].size > 3 | |
IRubyObject[] args = {<%= v["args_alone"].map{|i| "JavaUtil.convertJavaToRuby(__ruby__, #{i})"}.join(", ") %>}; | |
<%= "return (#{v["return_type"].include?(".") ? v["return_type"] : v["return_type"].capitalize})" if v["return_type"] != "void" %>RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "<%=v["ruby_method"]%>", args)<%= ".toJava(#{v["return_type"]}.class)" if v["return_type"] != "void" %>; | |
% elsif v["args_alone"].size > 0 | |
<%= "return (#{v["return_type"].include?(".") ? v["return_type"] : v["return_type"].capitalize})" 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"%>; | |
% else | |
<%= "return (#{v["return_type"].include?(".") ? v["return_type"] : v["return_type"].capitalize})" if v["return_type"] != "void" %>RuntimeHelpers.invoke(__ruby__.getCurrentContext(), __this__, "<%=v["ruby_method"]%>")<%= ".toJava(#{v["return_type"]}.class)" if v["return_type"] != "void"%>; | |
% end | |
} catch (RaiseException re) { | |
re.printStackTrace(__ruby__.getErrorStream()); | |
} | |
} | |
<%= (v["return_type"] == "boolean" ? "return false;" : "return null;") unless v["return_type"] == "void" %> | |
} | |
% end | |
% end | |
%############################################################################################## | |
} |
Yes, the current one checked in was generated from a hand written configuration file designed to generate a RubotoAcrivity replacement for the old hand coded .java file. Once that was complete, I started working on the configuration file generated through reflection. It has about 100 more callbacks (and that's just the beginning). Now my challenge is to handle some differences namely:
- Remove callback that are hard coded
- Deal with callbacks that have duplicate names (e.g., onClick).
Basically, I want the interfaces file to be generated, then passed through some code (in the erb or separate rb) that polishes it, and then the RubotoActivity gets generated.
Alright. You should create a branch and push all the stuff you have related to this (I'd guess you have some kind of script for loading the callbacks from the yaml file and and having ERB compile it to the .java file. And maybe another script to generate the yaml file via reflection?) to that branch. Then I can help clean it up and eventually get it to the point where it can replace the current RubotoActivity.java, at which point we can merge the branch in. But I can't help much so long as parts of it are just on your computer
Is there a reason that when I added
to the beginning of RubotoActivity.erb and ran
erb RubotoActivity.erb > RubotoActivity.java
and then diffed that RubotoActivity.java with the one in the GitHub repository, I got a big diff?