Skip to content

Instantly share code, notes, and snippets.

@rscottm
Created June 14, 2010 21:02
Show Gist options
  • Save rscottm/438284 to your computer and use it in GitHub Desktop.
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
%##############################################################################################
}
@jackowayed
Copy link

Is there a reason that when I added

%    require 'yaml'
%    @callbacks = YAML::load File.read('interfaces.yml')

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?

@rscottm
Copy link
Author

rscottm commented Jun 15, 2010

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:

  1. Remove callback that are hard coded
  2. 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.

@jackowayed
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment