ruboto-core 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ gem 'main'
4
+ gem 'jruby-jars'
data/Gemfile.lock ADDED
@@ -0,0 +1,16 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ arrayfields (4.7.4)
5
+ fattr (2.1.0)
6
+ jruby-jars (1.5.3)
7
+ main (4.2.0)
8
+ arrayfields (>= 4.7.4)
9
+ fattr (>= 2.1.0)
10
+
11
+ PLATFORMS
12
+ java
13
+
14
+ DEPENDENCIES
15
+ jruby-jars
16
+ main
data/README.md CHANGED
@@ -31,6 +31,7 @@ Command-line Tools
31
31
 
32
32
  * [Application generator](#application_generator) (like the rails application generator)
33
33
  * [Class generator](#class_generator) to generate additional Activities, BroadcastReceivers, Services, etc.
34
+ * [Callback generator](#class_generator) to generate specific subclasses to open up access (callbacks) for various portions of the Android API.
34
35
  * [Packaging task](#packaging_task) to generate an apk file
35
36
  * [Deployment task](#deployment_task) to deploy a generated package to an emulator or connected device
36
37
  * [Develop without having to compile to try every change](#update_scripts)
@@ -39,16 +40,52 @@ Command-line Tools
39
40
  <a name="application_generator"></a>
40
41
  ### Application generator
41
42
 
42
- $ ruboto gen app --package com.yourdomain.whatever --path path/to/where/you/want/the/app --name NameOfApp --target android-version --activity MainActivityName
43
- Currently any value but `android-8` will not work.
43
+ $ ruboto gen app --package com.yourdomain.whatever --path path/to/where/you/want/the/app --name NameOfApp --target android-version --min_sdk another-android-version --activity MainActivityName
44
+ Version values must be specified using'android-' and the sdk level number (e.g., android-8 is froyo).
44
45
 
45
46
  <a name="class_generator"></a>
46
47
  ### Class generator
47
48
 
49
+ Generates a Java class (Activity, Service, or BroadcastReceiver) associated with a specific ruboto script.
50
+
48
51
  $ ruboto gen class ClassName --name YourObjectName
49
52
  Ex:
50
53
  $ ruboto gen class BroadcastReceiver --name AwesomenessReceiver
51
54
 
55
+ <a name="callback_generator"></a>
56
+ ### Callback generator
57
+
58
+ Can subclass any part of the Android API to pass control over to a script when the specified methods are called. You can also create classes that implement a single Android interface to pass control over to ruboto.
59
+
60
+ For classes that need subclassing (e.g., PhoneStateListener, SQLiteOpenHelper, View)
61
+
62
+ $ ruboto gen subclass AndroidPackageAndClassName --name YourClassName --method_base all-on-or-none --method_include methods --method_exclude methods
63
+ Ex:
64
+ $ ruboto gen class android.telephony.PhoneStateListener --name MyPhoneStateListener
65
+
66
+ For interfaces that need implementing (e.g., OnClickListener or SensorListener)
67
+
68
+ $ ruboto gen interface AndroidPackageAndInterfaceName --name YourClassName
69
+ Ex:
70
+ $ ruboto gen interface android.hardware.SensorListener --name MySensorListener
71
+
72
+ Inside your script use:
73
+
74
+ # note that this is different than java_import
75
+ ruboto_import "your.package.MySensorListener"
76
+
77
+ Later:
78
+
79
+ # Create the callback object
80
+ @sensor_listener = MySensorListener.new
81
+
82
+ # Specify the block to call
83
+ @sensor_listener.handle_sensor_changed do |sensor, values|
84
+ # Do stuff
85
+ end
86
+
87
+ # Register the listener
88
+
52
89
  <a name="packaging_task"></a>
53
90
  ### Packaging task
54
91
 
@@ -100,8 +137,33 @@ Also, you need root access to your device for this to work, as it needs to write
100
137
 
101
138
  ### Updating Ruboto's Files
102
139
 
103
- Not implemented, yet.
140
+ You can update various portions of your generated Ruboto app through the ruboto command:
141
+
142
+ * JRuby:
143
+
144
+ 1) If a new version of JRuby is released, you should update your gem (e.g., sudo gem update jruby-jars).
145
+
146
+ 2) From the root directory of your app:
147
+
148
+ $ ruboto update jruby
149
+
150
+ * The ruboto.rb script:
151
+
152
+ 1) From the root directory of your app:
153
+
154
+ $ ruboto update ruboto
155
+
156
+ * The core classes (e.g., RubotoActivity):
157
+
158
+ 1) These classes are generated on your machine based on the SDKs (min and target) specified when you 'gen app' (stored in the AndroidManifest.xml)
159
+
160
+ 2) You many want to regenerate them if a new version of the SDK is released, if you change your targets, or if you want more control over the callbacks you receive.
161
+
162
+ 3) From the root directory of your app:
163
+
164
+ $ ruboto gen core Activity --method_base all-on-or-none --method_include specific-methods-to-include --method_include specific-methods-to-exclude
104
165
 
166
+ 4) The generator will load up the SDK information and find the specified methods. The generator will abort around methods that were added or deprecated based on the SDK levels. You can either exclude those methods or add --force to create them anyway (added methods are created without calling super to avoid crashin on legacy hardware).
105
167
 
106
168
  Scripts
107
169
  -------
data/Rakefile CHANGED
@@ -5,140 +5,6 @@ task :gem do
5
5
  end
6
6
 
7
7
  task :release do
8
- `gem push ruboto-core-0.0.1.gem`
9
- end
10
-
11
- require 'erb'
12
-
13
- def unprefixed_class(class_name)
14
- /\.([^\.]+)\z/.match(class_name)[1]
15
- end
16
-
17
- # active_support/inflector.rb
18
- def underscore(camel_cased_word)
19
- camel_cased_word.to_s.gsub(/::/, '/').
20
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
21
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
22
- tr("-", "_").
23
- downcase
24
- end
25
-
26
- def transform_return_type(type)
27
- if type.include?(".")
28
- return type
29
- elsif type == "int"
30
- return "Integer"
31
- else
32
- return type.capitalize
33
- end
34
- end
35
-
36
- task :generate_java_classes do
37
- all_callbacks = eval(IO.read("lib/java_class_gen/interfaces.txt"))
38
-
39
- @starting_methods = {"BroadcastReceiver" => "onReceive"}
40
- @starting_methods.default = "onCreate"
41
-
42
- all_callbacks.each do |full_class, method_hash|
43
- @class = unprefixed_class full_class
44
- @callbacks = method_hash
45
- @full_class = full_class
46
- @first_method = @starting_methods[@class]
47
-
48
-
49
- ##############################################################################################
50
- #
51
- # This code resolves any issues with the generated callbacks.
52
- #
53
- # 1) Remove callbacks that are hard coded in RubotoActivity.erb:
54
- #
55
-
56
- if @class == "Activity"
57
- @callbacks["android.view.View$OnCreateContextMenuListener"].delete("onCreateContextMenu")
58
- #
59
- # 2) Remove callbacks that are causing a problem
60
- #
61
- @callbacks["android.app.Activity"].delete("onRetainNonConfigurationChildInstances")
62
- #
63
- # 3) Override the callback constant for a few key callbacks
64
- #
65
- @callbacks["android.app.Activity"]["onMenuItemSelected"]["constant"] = "CB_CREATE_OPTIONS_MENU"
66
- @callbacks["android.app.Activity"]["onContextItemSelected"]["constant"] = "CB_CREATE_CONTEXT_MENU"
67
- #
68
- # 4) Create a unique name for callbacks that have duplicate names
69
- #
70
- @callbacks["android.content.DialogInterface$OnClickListener"]["onClick"]["ruby_method"] = "on_dialog_click"
71
- @callbacks["android.content.DialogInterface$OnClickListener"]["onClick"]["constant"] = "CB_DIALOG_CLICK"
72
- @callbacks["android.content.DialogInterface$OnKeyListener"]["onKey"]["ruby_method"] = "on_dialog_key"
73
- @callbacks["android.content.DialogInterface$OnKeyListener"]["onKey"]["constant"] = "CB_DIALOG_KEY"
74
- @callbacks["android.content.DialogInterface$OnMultiChoiceClickListener"]["onClick"]["ruby_method"] = "on_dialog_multi_choice_click"
75
- @callbacks["android.content.DialogInterface$OnMultiChoiceClickListener"]["onClick"]["constant"] = "CB_DIALOG_MULTI_CHOICE_CLICK"
76
- #
77
- # 5) Report any duplicate name callbacks not handled
78
- #
79
- callbacks = {}
80
- @callbacks.each do |interface,i_info|
81
- i_info.each do |method,v|
82
- if callbacks[method] and not v["ruby_method"]
83
- puts "#{method} in #{interface} and #{callbacks[method]}"
84
- else
85
- callbacks[v["ruby_method"] || method] = interface
86
- end
87
- end
88
- end
89
- #
90
- # 6) Create a few new special case callbacks
91
- #
92
- @callbacks["none"] = {
93
- "onDraw" => {"args" => ["android.view.View", "android.graphics.Canvas"]},
94
- "onSizeChanged" => {"args" => ["android.view.View", "int", "int", "int", "int"]}
95
- }
96
- end
97
- #
98
- ##############################################################################################
99
- #
100
- # This code takes the callbacks hash (read out of the interfaces.txt file) and prepares
101
- # it for use in the code below.
102
- #
103
- @implements = []
104
- @constants = []
105
- @callbacks.each do |interface,i_info|
106
- i_info.each do |method,v|
107
- v["interface"] = interface.gsub("$", ".")
108
- v["interface"] = "Activity" if v["interface"] == "android.app.Activity"
109
- v["method"] = method
110
- v["return_type"] = (v["return_type"] || "void").gsub("$", ".")
111
- v["interface_method"] = v["interface_method"] || v["method"]
112
- v["ruby_method"] = v["ruby_method"] || v["method"].gsub(/[A-Z]/) {|i| "_#{i.downcase}"}
113
-
114
- @implements << v["interface"] if v["interface"] != full_class and
115
- v["interface"] != @class and
116
- v["interface"] != "none" and
117
- not @implements.include?(v["interface"])
118
-
119
- unless v["constant"]
120
- constant = v["method"].gsub(/[A-Z]/) {|i| "_#{i}"}.upcase
121
- constant = constant[3..-1] if constant[0..2] == "ON_"
122
- v["constant"] = "CB_#{constant}"
123
- end
124
- @constants << v["constant"] unless @constants.include?(v["constant"]) || v["method"] == @first_method
125
-
126
- v["args"] = (v["args"] || [])
127
- v["args_with_types"], v["args_alone"] = [], []
128
- v["args"].each_with_index {|arg_type, i| v["args_with_types"] << "#{arg_type.gsub("$", ".")} arg#{i}"; v["args_alone"] << "arg#{i}"}
129
- v["args_with_types"] = v["args_with_types"].join(", ")
130
- end
131
- end
132
- ##############################################################################################
133
-
134
-
135
- File.open("assets/src/Inheriting#{@class}.java", "w") do |file|
136
- file.write ERB.new(IO.read("lib/java_class_gen/InheritingClass.java.erb"), 0, "%").result
137
- end
138
-
139
- File.open("assets/src/org/ruboto/Ruboto#{@class}.java", "w") do |file|
140
- file.write ERB.new(IO.read("lib/java_class_gen/RubotoClass.java.erb"), 0, "%").result
141
- end
142
- end
8
+ `gem push #{Dir['ruboto-core-*.gem'][-1]}`
143
9
  end
144
10
 
data/assets/Rakefile CHANGED
@@ -29,6 +29,8 @@ file jruby_ruboto_jar => generated_libs do
29
29
  ant.zip(:destfile=>jruby_ruboto_jar) do
30
30
  zipfileset(:src=>jruby_jar) do
31
31
  exclude(:name=>'jni/**')
32
+ # dx chokes on some of the netdb classes, for some reason
33
+ exclude(:name=>'jnr/netdb/**')
32
34
  end
33
35
  end
34
36
  end
@@ -2,11 +2,14 @@
2
2
  #
3
3
  # ruboto.rb (by Scott Moyer)
4
4
  #
5
- # Wrapper for using RubotoActivity in Ruboto IRB
5
+ # - Wrapper for using RubotoActivity, RubotoService, and
6
+ # RubotoBroadcastReceiver.
7
+ # - Provides interface for generating UI elements.
8
+ # - Imports and configures callback classes.
6
9
  #
7
10
  #######################################################
8
11
 
9
- $RUBOTO_VERSION = 4
12
+ $RUBOTO_VERSION = 6
10
13
 
11
14
  def confirm_ruboto_version(required_version, exact=true)
12
15
  raise "requires $RUBOTO_VERSION=#{required_version} or greater, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION < required_version and not exact
@@ -15,9 +18,9 @@ end
15
18
 
16
19
  require 'java'
17
20
 
21
+ $package_name = "THE_PACKAGE"
18
22
 
19
-
20
- %w(Activity Dialog BroadcastReceiver Service View).map do |klass|
23
+ %w(Activity Dialog BroadcastReceiver Service).map do |klass|
21
24
  java_import "org.ruboto.Ruboto#{klass}"
22
25
  end
23
26
 
@@ -42,9 +45,9 @@ java_import "android.R"
42
45
  java_import "android.util.Log"
43
46
 
44
47
  module Ruboto
45
- java_import "THE_PACKAGE.R"
48
+ java_import "#{$package_name}.R"
46
49
  begin
47
- Id = JavaUtilities.get_proxy_class("THE_PACKAGE.R$id")
50
+ Id = JavaUtilities.get_proxy_class("#{$package_name}.R$id")
48
51
  rescue NameError
49
52
  Log.d "RUBOTO", "no R$id"
50
53
  end
@@ -58,16 +61,14 @@ AndroidIds = JavaUtilities.get_proxy_class("android.R$id")
58
61
  #
59
62
 
60
63
  class Activity
61
- attr_accessor :init_block
62
-
63
64
  def start_ruboto_dialog(remote_variable, &block)
64
65
  start_ruboto_activity(remote_variable, RubotoDialog, &block)
65
66
  end
66
67
 
67
68
  def start_ruboto_activity(remote_variable, klass=RubotoActivity, &block)
68
- @@init_block = block
69
+ $activity_init_block = block
69
70
 
70
- if @initialized or not self.is_a?(RubotoActivity)
71
+ if @initialized or not self.kind_of?(RubotoActivity)
71
72
  b = Bundle.new
72
73
  b.putString("Remote Variable", remote_variable)
73
74
  b.putBoolean("Define Remote Variable", true)
@@ -101,112 +102,134 @@ end
101
102
 
102
103
  #############################################################################
103
104
  #
104
- # RubotoActivity
105
+ # Configure a class to work with handlers
105
106
  #
106
107
 
107
- class RubotoActivity
108
- #
109
- # Initialize
110
- #
111
-
112
- def initialize_activity()
113
- instance_eval &@@init_block
114
- @initialized = true
115
- self
116
- end
117
-
118
- #
119
- # Option Menus
120
- #
121
-
122
- def add_menu title, icon=nil, &block
123
- mi = @menu.add(title)
124
- mi.setIcon(icon) if icon
125
- mi.class.class_eval {attr_accessor :on_click}
126
- mi.on_click = block
127
- end
128
-
129
- def handle_create_options_menu &block
130
- requestCallback RubotoActivity::CB_CREATE_OPTIONS_MENU
131
- @create_options_menu_block = block
132
- end
133
-
134
- def on_create_options_menu(*args)
135
- @menu, @context_menu = args[0], nil
136
- instance_eval {@create_options_menu_block.call(*args)} if @create_options_menu_block
137
- end
138
-
139
- def on_menu_item_selected(num,menu_item)
140
- (instance_eval &(menu_item.on_click); return true) if @menu
141
- false
142
- end
108
+ def ruboto_allow_handlers(klass)
109
+ klass.class_eval do
110
+ def method_missing(name, *args, &block)
111
+ if name.to_s =~ /^handle_(.*)/ and (const = self.class.const_get("CB_#{$1.upcase}"))
112
+ setCallbackProc(const, block)
113
+ self
114
+ else
115
+ super
116
+ end
117
+ end
143
118
 
144
- #
145
- # Context Menus
146
- #
119
+ def respond_to?(name)
120
+ return true if name.to_s =~ /^handle_(.*)/ and self.class.const_get("CB_#{$1.upcase}")
121
+ super
122
+ end
147
123
 
148
- def add_context_menu title, &block
149
- mi = @context_menu.add(title)
150
- mi.class.class_eval {attr_accessor :on_click}
151
- mi.on_click = block
124
+ def initialize_handlers(&block)
125
+ instance_eval &block
126
+ self
127
+ end
152
128
  end
129
+ klass
130
+ end
153
131
 
154
- def handle_create_context_menu &block
155
- requestCallback RubotoActivity::CB_CREATE_CONTEXT_MENU
156
- @create_context_menu_block = block
157
- end
132
+ #############################################################################
133
+ #
134
+ # Activity Subclass Setup
135
+ #
158
136
 
159
- def on_create_context_menu(*args)
160
- @menu, @context_menu = nil, args[0]
161
- instance_eval {@create_context_menu_block.call(*args)} if @create_context_menu_block
162
- end
137
+ def ruboto_configure_activity(klass)
138
+ klass.class_eval do
139
+ #
140
+ # Initialize
141
+ #
163
142
 
164
- def on_context_item_selected(menu_item)
165
- (instance_eval {menu_item.on_click.call(menu_item.getMenuInfo.position)}; return true) if menu_item.on_click
166
- false
167
- end
168
- end
143
+ def initialize_activity()
144
+ instance_eval &$activity_init_block
145
+ @initialized = true
146
+ self
147
+ end
169
148
 
170
- RUBOTO_CLASSES.each do |klass|
171
- klass.class_eval do
172
- def when_launched(&block)
173
- instance_exec *args, &block
174
- on_create nil
149
+ def handle_finish_create &block
150
+ @finish_create_block = block
175
151
  end
176
152
 
177
- def handle_create &block
178
- @create_block = block
153
+ def setup_content &block
154
+ @content_view_block = block
179
155
  end
180
156
 
181
157
  def on_create(bundle)
158
+ @view_parent = nil
182
159
  setContentView(instance_eval &@content_view_block) if @content_view_block
183
- instance_eval {@create_block.call} if @create_block
160
+ instance_eval {@finish_create_block.call} if @finish_create_block
184
161
  end
185
162
 
186
- # plugin or something
187
- def setup_content &block
188
- @view_parent = nil
189
- @content_view_block = block
163
+ #
164
+ # Option Menus
165
+ #
166
+
167
+ def add_menu title, icon=nil, &block
168
+ mi = @menu.add(title)
169
+ mi.setIcon(icon) if icon
170
+ mi.class.class_eval {attr_accessor :on_click}
171
+ mi.on_click = block
172
+
173
+ # Seems to be needed or the block might get cleaned up
174
+ @all_menu_items = [] unless @all_menu_items
175
+ @all_menu_items << mi
176
+ end
177
+
178
+ def handle_create_options_menu &block
179
+ p = Proc.new do |*args|
180
+ @menu, @context_menu = args[0], nil
181
+ instance_eval {block.call(*args)} if block
182
+ end
183
+ setCallbackProc(self.class.const_get("CB_CREATE_OPTIONS_MENU"), p)
184
+
185
+ p = Proc.new do |num,menu_item|
186
+ (instance_eval &(menu_item.on_click); return true) if @menu
187
+ false
188
+ end
189
+ setCallbackProc(self.class.const_get("CB_MENU_ITEM_SELECTED"), p)
190
190
  end
191
191
 
192
192
  #
193
- # Setup Callbacks
193
+ # Context Menus
194
194
  #
195
195
 
196
- def method_missing(name, *args, &block)
197
- # make #handle_name_of_callback request that callback
198
- if name.to_s =~ /^handle_(.*)/ and (const = RubotoActivity.const_get("CB_#{$1.upcase}"))
199
- requestCallback const
200
- @eigenclass ||= class << self; self; end
201
- @eigenclass.send(:define_method, "on_#{$1}", &block)
202
- else
203
- super
196
+ def add_context_menu title, &block
197
+ mi = @context_menu.add(title)
198
+ mi.class.class_eval {attr_accessor :on_click}
199
+ mi.on_click = block
200
+
201
+ # Seems to be needed or the block might get cleaned up
202
+ @all_menu_items = [] unless @all_menu_items
203
+ @all_menu_items << mi
204
+ end
205
+
206
+ def handle_create_context_menu &block
207
+ p = Proc.new do |*args|
208
+ @menu, @context_menu = nil, args[0]
209
+ instance_eval {block.call(*args)} if block
204
210
  end
211
+ setCallbackProc(self.class.const_get("CB_CREATE_CONTEXT_MENU"), p)
212
+
213
+ p = Proc.new do |menu_item|
214
+ (instance_eval {menu_item.on_click.call(menu_item.getMenuInfo.position)}; return true) if menu_item.on_click
215
+ false
216
+ end
217
+ setCallbackProc(self.class.const_get("CB_CONTEXT_ITEM_SELECTED"), p)
205
218
  end
219
+ end
206
220
 
207
- def respond_to?(name)
208
- return true if name.to_s =~ /^handle_(.*)/ and RubotoActivity.const_get("CB_#{$1.upcase}")
209
- super
221
+ ruboto_allow_handlers(klass)
222
+ end
223
+ ruboto_configure_activity(RubotoActivity)
224
+
225
+ RUBOTO_CLASSES.each do |klass|
226
+ # Setup ability to handle callbacks
227
+ ruboto_allow_handlers(klass)
228
+
229
+ klass.class_eval do
230
+ def when_launched(&block)
231
+ instance_exec *args, &block
232
+ on_create nil
210
233
  end
211
234
 
212
235
  eval %Q{
@@ -217,7 +240,6 @@ RUBOTO_CLASSES.each do |klass|
217
240
  end
218
241
  end
219
242
 
220
-
221
243
  #############################################################################
222
244
  #
223
245
  # RubotoActivity View Generation
@@ -227,8 +249,8 @@ def ruboto_import_widgets(*widgets)
227
249
  widgets.each{|i| ruboto_import_widget i}
228
250
  end
229
251
 
230
- def ruboto_import_widget(class_name)
231
- view_class = java_import "android.widget.#{class_name}"
252
+ def ruboto_import_widget(class_name, package_name="android.widget")
253
+ view_class = java_import "#{package_name}.#{class_name}"
232
254
  return unless view_class
233
255
 
234
256
  RubotoActivity.class_eval "
@@ -246,14 +268,14 @@ def ruboto_import_widget(class_name)
246
268
  "
247
269
  end
248
270
 
249
- # Need to load these two to extend classes
250
- ruboto_import_widgets :ListView, :Button
251
-
252
271
  #############################################################################
253
272
  #
254
273
  # Extend Common View Classes
255
274
  #
256
275
 
276
+ # Need to load these two to extend classes
277
+ ruboto_import_widgets :ListView, :Button
278
+
257
279
  class View
258
280
  @@convert_params = {
259
281
  :wrap_content => ViewGroup::LayoutParams::WRAP_CONTENT,
@@ -308,3 +330,127 @@ class Button
308
330
  super(context, params)
309
331
  end
310
332
  end
333
+
334
+ #############################################################################
335
+ #
336
+ # Import a class and set it up for handlers
337
+ #
338
+
339
+ def ruboto_import(package_class)
340
+ klass = java_import package_class
341
+ return unless klass
342
+ ruboto_allow_handlers(klass)
343
+ end
344
+
345
+ #############################################################################
346
+ #
347
+ # Allows RubotoActivity to handle callbacks covering Class based handlers
348
+ #
349
+
350
+ def ruboto_register_handler(handler_class, unique_name, for_class, method_name)
351
+ klass_name = handler_class[/.+\.([A-Z].+)/,1]
352
+ klass = ruboto_import handler_class
353
+ return unless klass
354
+
355
+ RubotoActivity.class_eval "
356
+ attr_accessor :#{unique_name}_handler
357
+
358
+ def #{unique_name}_handler
359
+ @#{unique_name}_handler ||= #{klass_name}.new
360
+ end
361
+
362
+ def handle_#{unique_name}(&block)
363
+ #{unique_name}_handler.handle_#{unique_name} &block
364
+ self
365
+ end
366
+ "
367
+
368
+ for_class.class_eval "
369
+ alias_method :orig_#{method_name}, :#{method_name}
370
+ def #{method_name}(handler)
371
+ orig_#{method_name}(handler.kind_of?(RubotoActivity) ? handler.#{unique_name}_handler : handler)
372
+ end
373
+ "
374
+ end
375
+
376
+ ruboto_register_handler("org.ruboto.callbacks.RubotoOnClickListener", "click", Button, "setOnClickListener")
377
+ ruboto_register_handler("org.ruboto.callbacks.RubotoOnItemClickListener", "item_click", ListView, "setOnItemClickListener")
378
+
379
+ #############################################################################
380
+ #
381
+ # RubotoPreferenceActivity Preference Generation
382
+ #
383
+
384
+ def ruboto_import_preferences(*preferences)
385
+ preferences.each{|i| ruboto_import_preference i}
386
+ end
387
+
388
+ def ruboto_import_preference(class_name, package_name="android.preference")
389
+ klass = java_import "#{package_name}.#{class_name}"
390
+ return unless klass
391
+
392
+ setup_preferences
393
+
394
+ RubotoPreferenceActivity.class_eval "
395
+ def #{(class_name.to_s.gsub(/([A-Z])/) {'_' + $1.downcase})[1..-1]}(params={})
396
+ rv = #{class_name}.new self
397
+ rv.configure self, params
398
+ @parent.addPreference(rv) if @parent
399
+ if block_given?
400
+ old_parent, @parent = @parent, rv
401
+ yield
402
+ @parent = old_parent
403
+ end
404
+ rv
405
+ end
406
+ "
407
+ end
408
+
409
+ def setup_preferences
410
+ return if @preferences_setup_complete
411
+
412
+ java_import "android.preference.PreferenceScreen"
413
+ java_import "android.preference.Preference"
414
+ java_import "org.ruboto.RubotoPreferenceActivity"
415
+ ruboto_configure_activity(RubotoPreferenceActivity)
416
+
417
+
418
+ RubotoPreferenceActivity.class_eval do
419
+ def preference_screen(params={})
420
+ rv = self.getPreferenceManager.createPreferenceScreen(self)
421
+ rv.configure self, params
422
+ @parent.addPreference(rv) if @parent
423
+ if block_given?
424
+ old_parent, @parent = @parent, rv
425
+ yield
426
+ @parent = old_parent
427
+ end
428
+ rv
429
+ end
430
+
431
+ def setup_preference_screen &block
432
+ @preference_screen_block = block
433
+ end
434
+
435
+ def on_create(bundle)
436
+ @parent = nil
437
+ setPreferenceScreen(instance_eval &@preference_screen_block) if @preference_screen_block
438
+ instance_eval {@finish_create_block.call} if @finish_create_block
439
+ end
440
+ end
441
+
442
+ Preference.class_eval do
443
+ def configure(context, params = {})
444
+ params.each do |k, v|
445
+ if v.is_a?(Array)
446
+ self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) {$2.upcase}}", *v)
447
+ else
448
+ self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) {$2.upcase}}", v)
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+ @preferences_setup_complete = true
455
+ end
456
+