ruboto-core 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -3,13 +3,14 @@ GEM
3
3
  specs:
4
4
  arrayfields (4.7.4)
5
5
  fattr (2.1.0)
6
- jruby-jars (1.5.6)
6
+ jruby-jars (1.6.3)
7
7
  main (4.2.0)
8
8
  arrayfields (>= 4.7.4)
9
9
  fattr (>= 2.1.0)
10
10
 
11
11
  PLATFORMS
12
12
  java
13
+ ruby
13
14
 
14
15
  DEPENDENCIES
15
16
  jruby-jars
data/README.md CHANGED
@@ -163,7 +163,7 @@ You can update various portions of your generated Ruboto app through the ruboto
163
163
 
164
164
  $ ruboto gen core Activity --method_base all-on-or-none --method_include specific-methods-to-include --method_include specific-methods-to-exclude
165
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).
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 use method_exclude to remove methods individually or add '--force exclude' to remove the all. You can also us '--force include' to create them anyway (added methods are created without calling super to avoid crashin on legacy hardware).
167
167
 
168
168
  Scripts
169
169
  -------
data/Rakefile CHANGED
@@ -5,11 +5,11 @@ task :gem do
5
5
  end
6
6
 
7
7
  task :release do
8
- `gem push #{Dir['ruboto-core-*.gem'][-1]}`
8
+ sh "gem push #{Dir['ruboto-core-*.gem'][-1]}"
9
9
  end
10
10
 
11
11
  task :test do
12
12
  Dir['test/*_test.rb'].each do |f|
13
- load f
13
+ require f.chomp('.rb')
14
14
  end
15
15
  end
data/assets/Rakefile CHANGED
@@ -1,59 +1,35 @@
1
- # callback_reflection.rb creates the interfaces.txt (JRuby can't do YAML with ruby 1.8, so it's just
2
- # and inspect on the hash) on a device. Bring it off the device and put it in the callback_gen dir.
3
- #
4
- # Move this into a rake task later.
5
- #
1
+ require 'time'
6
2
 
3
+ def manifest() @manifest ||= REXML::Document.new(File.read(MANIFEST_FILE)) end
4
+ def package() manifest.root.attribute('package') end
5
+ def build_project_name() @build_project_name ||= REXML::Document.new(File.read('build.xml')).elements['project'].attribute(:name).value end
7
6
 
8
- if RUBY_PLATFORM =~ /java/
9
- puts "Detected JRuby. Loading ANT."
10
- require 'ant'
11
- ant_loaded = true
12
- end
7
+ UPDATE_MARKER_FILE = File.expand_path(File.join('tmp', 'LAST_UPDATE'), File.dirname(__FILE__))
8
+ BUNDLE_JAR = 'libs/bundle.jar'
9
+ BUNDLE_PATH = 'tmp/bundle'
13
10
 
14
11
  require 'rake/clean'
15
12
  require 'rexml/document'
16
13
 
17
- generated_libs = 'generated_libs'
18
14
  jars = Dir['libs/*.jar']
19
15
  stdlib = jars.grep(/stdlib/).first #libs/jruby-stdlib-VERSION.jar
20
16
  jruby_jar = jars.grep(/core/).first #libs/jruby-core-VERSION.jar
21
- stdlib_precompiled = File.join(generated_libs, 'jruby-stdlib-precompiled.jar')
22
- jruby_ruboto_jar = File.join(generated_libs, 'jruby-ruboto.jar')
23
- ant.property :name=>'external.libs.dir', :value => generated_libs if ant_loaded
24
- dirs = ['tmp/ruby', 'tmp/precompiled', generated_libs]
25
- dirs.each { |d| directory d }
26
-
27
- CLEAN.include('tmp', 'bin', generated_libs)
28
-
29
- ant_import if ant_loaded
30
-
31
- file stdlib_precompiled => :compile_stdlib
32
- file jruby_ruboto_jar => generated_libs do
33
- ant.zip(:destfile=>jruby_ruboto_jar) do
34
- zipfileset(:src=>jruby_jar) do
35
- exclude(:name=>'jni/**')
36
- # dx chokes on some of the netdb classes, for some reason
37
- exclude(:name=>'jnr/netdb/**')
38
- end
39
- end
40
- end
17
+ MANIFEST_FILE = 'AndroidManifest.xml'
18
+ APK_FILE = "bin/#{build_project_name}-debug.apk"
41
19
 
42
- desc "precompile ruby stdlib"
43
- task :compile_stdlib => [:clean, *dirs] do
44
- ant.unzip(:src=>stdlib, :dest=>'tmp/ruby')
45
- Dir.chdir('tmp/ruby') { sh "jrubyc . -t ../precompiled" }
46
- ant.zip(:destfile=>stdlib_precompiled, :basedir=>'tmp/precompiled')
47
- end
20
+ CLEAN.include('tmp', 'bin')
48
21
 
49
- task :generate_libs => [generated_libs, jruby_ruboto_jar] do
50
- cp stdlib, generated_libs
51
- end
22
+ task :default => :debug
52
23
 
53
- task :debug => :generate_libs
54
- task :release => :generate_libs
24
+ desc 'build debug package'
25
+ task :debug => [BUNDLE_JAR, :mark_update] do
26
+ sh 'ant debug'
27
+ end
55
28
 
56
- task :default => :debug
29
+ desc "build package and install it on the emulator or device"
30
+ task :install => [BUNDLE_JAR, :mark_update] do
31
+ sh 'ant install'
32
+ end
57
33
 
58
34
  task :tag => :release do
59
35
  unless `git branch` =~ /^\* master$/
@@ -78,6 +54,11 @@ task :publish => :align do
78
54
  puts "#{build_project_name}.apk is ready for the market!"
79
55
  end
80
56
 
57
+ desc 'Start the emulator with larger disk'
58
+ task :emulator do
59
+ system 'emulator -partition-size 1024 -avd Android_3.0'
60
+ end
61
+
81
62
  task :start_app do
82
63
  `adb shell am start -a android.intent.action.MAIN -n #{package}/.#{main_activity}`
83
64
  end
@@ -86,33 +67,65 @@ task :stop_app do
86
67
  `adb shell ps | grep #{package} | awk '{print $2}' | xargs adb shell kill`
87
68
  end
88
69
 
70
+ desc 'Restart the application'
89
71
  task :restart => [:stop_app, :start_app]
90
72
 
91
73
  task :uninstall do
74
+ puts "Uninstalling package #{package}"
92
75
  system "adb uninstall #{package}"
93
76
  end
94
77
 
95
78
  namespace :install do
79
+ desc 'Uninstall, build, and install the application'
80
+ task :clean => [:uninstall, :install]
81
+
96
82
  desc 'Build, install, and restart the application'
97
83
  task :restart => [:install, :start_app]
84
+
98
85
  namespace :restart do
99
86
  desc 'Uninstall, build, install, and restart the application'
100
87
  task :clean => [:uninstall, :install, :start_app]
101
88
  end
102
89
  end
103
90
 
104
- desc 'Copy scripts to emulator'
105
- task :update_scripts do
106
- manifest
91
+ file MANIFEST_FILE
92
+
93
+ file APK_FILE => MANIFEST_FILE do
94
+ puts "#{MANIFEST_FILE} changed. Forcing rebuild of #{APK_FILE}."
95
+ Rake::Task['install:clean'].invoke
96
+ end
97
+
98
+ desc 'Copy scripts to emulator or device'
99
+ task :update_scripts => [BUNDLE_JAR, APK_FILE] do
100
+ sdcard_path = "/mnt/sdcard/Android/data/#{package}/files"
101
+ app_files_path = "/data/data/#{package}/files"
102
+ if package_installed? && device_path_exists?(sdcard_path)
103
+ puts 'Pushing files to apk public file area.'
104
+ data_dir = sdcard_path
105
+ elsif package_installed? && device_path_exists?(app_files_path)
106
+ puts 'Pushing files to apk private file area.'
107
+ data_dir = app_files_path
108
+ else
109
+ puts 'Cannot find the scripts directory on the device.'
110
+ puts 'If you have a non-rooted device, you need to add'
111
+ puts %q{ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}
112
+ puts 'to the AndroidManifest.xml file to enable the update_scripts rake task.'
113
+ puts "Reverting to uninstalling and re-installing the apk."
114
+ Rake::Task['install:clean'].invoke
115
+ next
116
+ end
117
+ last_update = File.exists?(UPDATE_MARKER_FILE) ? Time.parse(File.read(UPDATE_MARKER_FILE)) : Time.parse('1970-01-01T00:00:00')
107
118
  Dir.chdir('assets') do
108
- # Add more directories here if you want them copied to the emulator
109
- ['scripts'].each do |script_dir|
110
- Dir["#{script_dir}/*"].each do |script|
111
- next if File.directory? script
112
- `adb push #{script} /data/data/#{package}/files/#{script_dir}`
119
+ ['scripts'].each do |asset_dir|
120
+ Dir["#{asset_dir}/*"].each do |asset_file|
121
+ next if File.directory? asset_file
122
+ next if File.ctime(asset_file) < last_update
123
+ print "#{asset_file}: " ; $stdout.flush
124
+ `adb push #{asset_file} #{data_dir}/#{asset_file}`
113
125
  end
114
126
  end
115
127
  end
128
+ mark_update
116
129
  end
117
130
 
118
131
  namespace :update_scripts do
@@ -121,10 +134,19 @@ namespace :update_scripts do
121
134
  end
122
135
 
123
136
  task :update_test_scripts do
124
- Dir['test/assets/scripts/*.rb'].each do |script|
125
- `adb push #{script} /data/data/#{package}.tests/files/scripts/`
137
+ test_scripts_path = "/data/data/#{package}.tests/files/scripts"
138
+ # TODO(uwe): Investigate if we can just push the scripts instead of building and installing the instrumentation APK
139
+ if false && package_installed?(true) && device_path_exists?(test_scripts_path)
140
+ Dir['test/assets/scripts/*.rb'].each do |script|
141
+ print "#{script}: " ; $stdout.flush
142
+ `adb push #{script} #{test_scripts_path}`
143
+ end
144
+ `adb shell ps | grep #{package}.tests | awk '{print $2}' | xargs adb shell kill`
145
+ else
146
+ Dir.chdir 'test' do
147
+ sh 'ant install'
148
+ end
126
149
  end
127
- `adb shell ps | grep #{package}.tests | awk '{print $2}' | xargs adb shell kill`
128
150
  end
129
151
 
130
152
  task :test => :uninstall do
@@ -139,13 +161,74 @@ namespace :test do
139
161
  task :quick => [:update_scripts, :update_test_scripts] do
140
162
  Dir.chdir('test') do
141
163
  puts 'Running quick tests'
142
- system "ant run-tests-quick"
164
+ sh "ant run-tests-quick"
165
+ end
166
+ end
167
+ end
168
+
169
+ file 'Gemfile'
170
+
171
+ desc 'Generate bundle jar from Gemfile'
172
+ file BUNDLE_JAR => 'Gemfile' do
173
+ next unless File.exists? 'Gemfile'
174
+ puts "Generating #{BUNDLE_JAR}"
175
+
176
+ FileUtils.mkdir_p BUNDLE_PATH
177
+ sh "bundle install --path=#{BUNDLE_PATH}"
178
+
179
+ # FIXME(uwe): Should not be necessary. ARJDBC should not offer the same files as AR.
180
+
181
+ Dir.chdir "#{BUNDLE_PATH}/ruby/1.8/gems" do
182
+ scanned_files = []
183
+ Dir["*/lib/**/*"].each do |f|
184
+ raise "Malformed file name" unless f =~ %r{^(.*?)/lib/(.*)$}
185
+ gem_name, lib_file = $1, $2
186
+ if existing_file = scanned_files.find{|sf| sf =~ %r{(.*?)/lib/#{lib_file}}}
187
+ puts "Removing duplicate of file #{lib_file} in gem #{gem_name}"
188
+ puts "Already present in gem #{$1}"
189
+ end
143
190
  end
144
191
  end
192
+
193
+ # FIXME(uwe): Remove when directory listing in apk subdirectories work.
194
+ # FIXME(uwe): http://jira.codehaus.org/browse/JRUBY-5775
195
+ Dir["#{BUNDLE_PATH}/ruby/1.8/gems/activesupport-*/lib/active_support/core_ext.rb"].each do |faulty_file|
196
+ faulty_code = <<-'EOF'
197
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
198
+ require "active_support/core_ext/#{File.basename(path, '.rb')}"
145
199
  end
200
+ EOF
201
+ replace_faulty_code(faulty_file, faulty_code)
202
+ end
146
203
 
147
- def manifest
148
- @manifest ||= REXML::Document.new(File.read('AndroidManifest.xml'))
204
+ Dir["#{BUNDLE_PATH}/ruby/1.8/gems/activemodel-*/lib/active_model/validations.rb"].each do |faulty_file|
205
+ faulty_code = <<-EOF
206
+ Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
207
+ filename = File.basename(path)
208
+ require "active_model/validations/\#{filename}"
209
+ end
210
+ EOF
211
+ replace_faulty_code(faulty_file, faulty_code)
212
+ end
213
+
214
+ FileUtils.rm_f BUNDLE_JAR
215
+ Dir["#{BUNDLE_PATH}/ruby/1.8/gems/*"].each_with_index do |gem_dir, i|
216
+ `jar #{i == 0 ? 'c' : 'u'}f #{BUNDLE_JAR} -C #{gem_dir}/lib .`
217
+ end
218
+ FileUtils.rm_rf BUNDLE_PATH
219
+
220
+ Rake::Task['install'].invoke
221
+ end
222
+
223
+ task :mark_update do
224
+ mark_update
225
+ end
226
+
227
+ # Methods
228
+
229
+ def mark_update
230
+ FileUtils.mkdir_p File.dirname(UPDATE_MARKER_FILE)
231
+ File.open(UPDATE_MARKER_FILE, 'w'){|f| f << Time.now.iso8601}
149
232
  end
150
233
 
151
234
  def strings(name)
@@ -154,8 +237,46 @@ def strings(name)
154
237
  value.text
155
238
  end
156
239
 
157
- def package() manifest.root.attribute('package') end
158
240
  def version() strings :version_name end
241
+
159
242
  def app_name() strings :app_name end
160
- def main_activity() manifest.root.elements['application'].elements['activity'].attributes['android:name'] end
161
- def build_project_name() @build_project_name ||= REXML::Document.new(File.read('build.xml')).elements['project'].attribute(:name).value end
243
+
244
+ def main_activity() manifest.root.elements['application'].elements["activity[@android:label='@string/app_name']"].attribute('android:name') end
245
+
246
+ def device_path_exists?(path)
247
+ path_output =`adb shell ls #{path}`
248
+ result = path_output.chomp !~ /No such file or directory|opendir failed, Permission denied/
249
+ puts "Checking path on device: #{path}: #{result || path_output}"
250
+ result
251
+ end
252
+
253
+ def package_installed? test = false
254
+ package_name = "#{package}#{'.tests' if test}"
255
+ app_paths = ['', '-0', '-1', '-2'].map{|i| "/data/app/#{package_name}#{i}.apk"}
256
+ path_outputs = app_paths.map{|p| `adb shell ls #{p}`.chomp}
257
+ path_outputs.each_with_index do |o, i|
258
+ if o == app_paths[i]
259
+ puts "Found package #{app_paths[i]}"
260
+ return true
261
+ end
262
+ end
263
+ puts "Package not found: #{package_name}"
264
+ return false
265
+ end
266
+
267
+ private
268
+
269
+ def replace_faulty_code(faulty_file, faulty_code)
270
+ explicit_requires = Dir["#{faulty_file.chomp('.rb')}/*.rb"].sort.map{|f| File.basename(f)}.map do |filename|
271
+ "require 'active_model/validations/#{filename}'"
272
+ end.join("\n")
273
+
274
+ old_code = File.read(faulty_file)
275
+ new_code = old_code.gsub faulty_code, explicit_requires
276
+ if new_code != old_code
277
+ puts "Replaced directory listing code in file #{faulty_file} with explicit requires."
278
+ File.open(faulty_file, 'w'){|f| f << new_code}
279
+ else
280
+ puts "Could not find expected faulty code\n\n#{faulty_code}\n\nin file #{faulty_file}\n\n#{old_code}\n\n"
281
+ end
282
+ end
@@ -1,6 +1,6 @@
1
1
  #######################################################
2
2
  #
3
- # ruboto.rb (by Scott Moyer)
3
+ # ruboto.rb
4
4
  #
5
5
  # - Wrapper for using RubotoActivity, RubotoService, and
6
6
  # RubotoBroadcastReceiver.
@@ -9,7 +9,7 @@
9
9
  #
10
10
  #######################################################
11
11
 
12
- $RUBOTO_VERSION = 7
12
+ $RUBOTO_VERSION = 8
13
13
 
14
14
  def confirm_ruboto_version(required_version, exact=true)
15
15
  raise "requires $RUBOTO_VERSION=#{required_version} or greater, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION < required_version and not exact
@@ -18,7 +18,15 @@ end
18
18
 
19
19
  require 'java'
20
20
 
21
- $package_name = "THE_PACKAGE"
21
+ $package_name = ($activity || $service || $broadcast_receiver).package_name
22
+ $package = eval("Java::#{$package_name}")
23
+
24
+ # Create convenience method for top-level android package so we do not need to prefix with 'Java::'.
25
+ module Kernel
26
+ def android
27
+ JavaUtilities.get_package_module_dot_format('android')
28
+ end
29
+ end
22
30
 
23
31
  java_import "android.R"
24
32
 
@@ -32,53 +40,77 @@ module Ruboto
32
40
  end
33
41
  AndroidIds = JavaUtilities.get_proxy_class("android.R$id")
34
42
 
43
+ class Object
44
+ def with_large_stack(opts = {}, &block)
45
+ opts = {:size => opts} if opts.is_a? Integer
46
+ opts = {:name => 'Block with large stack'}.update(opts)
47
+ result = nil
48
+ t = Thread.with_large_stack(opts, &proc{result = block.call})
49
+ t.join
50
+ result
51
+ end
52
+ end
53
+
54
+ class Thread
55
+ def self.with_large_stack(opts = {}, &block)
56
+ opts = {:size => opts} if opts.is_a? Integer
57
+ stack_size_kb = opts.delete(:size) || 64
58
+ name = opts.delete(:name) || "Thread with large stack"
59
+ raise "Unknown option(s): #{opts.inspect}" unless opts.empty?
60
+ t = java.lang.Thread.new(nil, block, name, stack_size_kb * 1024)
61
+ t.start
62
+ t
63
+ end
64
+ end
65
+
35
66
  #############################################################################
36
67
  #
37
68
  # Activity
38
69
  #
39
70
 
40
71
  def setup_activity
41
- java_import "android.app.Activity"
42
-
43
- Activity.class_eval do
44
- def start_ruboto_dialog(remote_variable, &block)
45
- start_ruboto_activity(remote_variable, RubotoDialog, &block)
46
- end
72
+ java_import "android.app.Activity"
47
73
 
48
- def start_ruboto_activity(remote_variable, klass=RubotoActivity, &block)
49
- $activity_init_block = block
50
-
51
- if @initialized or self == $activity
52
- b = Java::android.os.Bundle.new
53
- b.putString("Remote Variable", remote_variable)
54
- b.putBoolean("Define Remote Variable", true)
55
- b.putString("Initialize Script", "#{remote_variable}.initialize_activity")
56
-
57
- i = Java::android.content.Intent.new
58
- i.setClass self, klass.java_class
59
- i.putExtra("RubotoActivity Config", b)
60
-
61
- self.startActivity i
62
- else
63
- instance_eval "#{remote_variable}=self"
64
- setRemoteVariable remote_variable
65
- initialize_activity
66
- on_create nil
74
+ Activity.class_eval do
75
+ def start_ruboto_dialog(remote_variable, theme=R.style::Theme_Dialog, &block)
76
+ start_ruboto_activity(remote_variable, RubotoDialog, theme, &block)
67
77
  end
68
78
 
69
- self
70
- end
79
+ def start_ruboto_activity(remote_variable, klass=RubotoActivity, theme=nil, &block)
80
+ $activity_init_block = block
71
81
 
72
- #plugin
73
- def toast(text, duration=5000)
74
- Java::android.widget.Toast.makeText(self, text, duration).show
75
- end
82
+ if @initialized or self == $activity
83
+ b = Java::android.os.Bundle.new
84
+ b.putInt("Theme", theme) if theme
85
+ b.putString("Remote Variable", remote_variable)
86
+ b.putBoolean("Define Remote Variable", true)
87
+ b.putString("Initialize Script", "#{remote_variable}.initialize_activity")
76
88
 
77
- #plugin
78
- def toast_result(result, success, failure, duration=5000)
79
- toast(result ? success : failure, duration)
89
+ i = Java::android.content.Intent.new
90
+ i.setClass self, klass.java_class
91
+ i.putExtra("RubotoActivity Config", b)
92
+
93
+ self.startActivity i
94
+ else
95
+ instance_eval "#{remote_variable}=self"
96
+ setRemoteVariable remote_variable
97
+ initialize_activity
98
+ on_create nil
99
+ end
100
+
101
+ self
102
+ end
103
+
104
+ #plugin
105
+ def toast(text, duration=5000)
106
+ Java::android.widget.Toast.makeText(self, text, duration).show
107
+ end
108
+
109
+ #plugin
110
+ def toast_result(result, success, failure, duration=5000)
111
+ toast(result ? success : failure, duration)
112
+ end
80
113
  end
81
- end
82
114
  end
83
115
 
84
116
  #############################################################################
@@ -138,7 +170,7 @@ def ruboto_configure_activity(klass)
138
170
  def on_create(bundle)
139
171
  @view_parent = nil
140
172
  setContentView(instance_eval &@content_view_block) if @content_view_block
141
- instance_eval {@finish_create_block.call} if @finish_create_block
173
+ instance_eval { @finish_create_block.call } if @finish_create_block
142
174
  end
143
175
 
144
176
  #
@@ -148,7 +180,7 @@ def ruboto_configure_activity(klass)
148
180
  def add_menu title, icon=nil, &block
149
181
  mi = @menu.add(title)
150
182
  mi.setIcon(icon) if icon
151
- mi.class.class_eval {attr_accessor :on_click}
183
+ mi.class.class_eval { attr_accessor :on_click }
152
184
  mi.on_click = block
153
185
 
154
186
  # Seems to be needed or the block might get cleaned up
@@ -158,14 +190,21 @@ def ruboto_configure_activity(klass)
158
190
 
159
191
  def handle_create_options_menu &block
160
192
  p = Proc.new do |*args|
161
- @menu, @context_menu = args[0], nil
162
- instance_eval {block.call(*args)} if block
193
+ @menu = args[0]
194
+ instance_eval { block.call(*args) } if block
163
195
  end
164
196
  setCallbackProc(self.class.const_get("CB_CREATE_OPTIONS_MENU"), p)
165
197
 
166
- p = Proc.new do |num,menu_item|
167
- (instance_eval &(menu_item.on_click); return true) if @menu
168
- false
198
+ p = Proc.new do |num, menu_item|
199
+ # handles a problem where this is called for context items
200
+ # JRUBY-5866 JRuby can't access nested Java class if the class is called 'id'
201
+ unless @just_processed_context_item == menu_item || menu_item.item_id == JavaUtilities.get_proxy_class('android.R$id').home
202
+ instance_eval &(menu_item.on_click)
203
+ @just_processed_context_item = nil
204
+ true
205
+ else
206
+ false
207
+ end
169
208
  end
170
209
  setCallbackProc(self.class.const_get("CB_MENU_ITEM_SELECTED"), p)
171
210
  end
@@ -176,7 +215,7 @@ def ruboto_configure_activity(klass)
176
215
 
177
216
  def add_context_menu title, &block
178
217
  mi = @context_menu.add(title)
179
- mi.class.class_eval {attr_accessor :on_click}
218
+ mi.class.class_eval { attr_accessor :on_click }
180
219
  mi.on_click = block
181
220
 
182
221
  # Seems to be needed or the block might get cleaned up
@@ -186,14 +225,24 @@ def ruboto_configure_activity(klass)
186
225
 
187
226
  def handle_create_context_menu &block
188
227
  p = Proc.new do |*args|
189
- @menu, @context_menu = nil, args[0]
190
- instance_eval {block.call(*args)} if block
228
+ @context_menu = args[0]
229
+ instance_eval { block.call(*args) } if block
191
230
  end
192
231
  setCallbackProc(self.class.const_get("CB_CREATE_CONTEXT_MENU"), p)
193
232
 
194
233
  p = Proc.new do |menu_item|
195
- (instance_eval {menu_item.on_click.call(menu_item.getMenuInfo.position)}; return true) if menu_item.on_click
196
- false
234
+ if menu_item.on_click
235
+ arg = menu_item
236
+ begin
237
+ arg = menu_item.getMenuInfo.position
238
+ rescue
239
+ end
240
+ instance_eval { menu_item.on_click.call(arg) }
241
+ @just_processed_context_item = menu_item
242
+ true
243
+ else
244
+ false
245
+ end
197
246
  end
198
247
  setCallbackProc(self.class.const_get("CB_CONTEXT_ITEM_SELECTED"), p)
199
248
  end
@@ -212,14 +261,10 @@ def ruboto_setup(klass, init_method="create")
212
261
  ruboto_allow_handlers(klass)
213
262
 
214
263
  klass.class_eval do
215
- def when_launched(&block)
216
- instance_exec *args, &block
217
- on_create nil
218
- end
219
-
220
264
  eval %Q{
221
265
  def handle_#{init_method}(&block)
222
- when_launched &block
266
+ instance_exec &block
267
+ #{klass == Java::org.ruboto.RubotoActivity ? "on_create(nil)" : ""}
223
268
  end
224
269
  }
225
270
  end
@@ -231,18 +276,43 @@ end
231
276
  #
232
277
 
233
278
  def ruboto_import_widgets(*widgets)
234
- widgets.each{|i| ruboto_import_widget i}
279
+ widgets.each { |i| ruboto_import_widget i }
235
280
  end
236
281
 
237
282
  def ruboto_import_widget(class_name, package_name="android.widget")
238
283
  view_class = java_import "#{package_name}.#{class_name}"
239
- return unless view_class
240
284
 
241
285
  RubotoActivity.class_eval "
242
- def #{(class_name.to_s.gsub(/([A-Z])/) {'_' + $1.downcase})[1..-1]}(params={})
243
- rv = #{class_name}.new self
244
- @view_parent.addView(rv) if @view_parent
286
+ def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={})
287
+ force_style = params.delete(:default_style)
288
+ force_parent = params.delete(:parent)
289
+ force_index = params.delete(:parent_index)
290
+ if force_style
291
+ if params.any?
292
+ attributes = android.util.AttributeSet.impl do |method, *args|
293
+ puts 'Not implemented, yet.'
294
+ puts %Q{Unhandled AttributeSet method: \#{method}(\#{args.map{|a| a.inspect}.join(', ')})}
295
+ end
296
+ else
297
+ attributes = nil
298
+ end
299
+ rv = #{class_name}.new(self, attributes, force_style)
300
+ else
301
+ if api_key = params.delete(:apiKey)
302
+ rv = #{class_name}.new(self, api_key)
303
+ else
304
+ rv = #{class_name}.new(self)
305
+ end
306
+ end
307
+
308
+ if force_index
309
+ (force_parent || @view_parent).addView(rv, force_index) if (force_parent || @view_parent)
310
+ else
311
+ (force_parent || @view_parent).addView(rv) if (force_parent || @view_parent)
312
+ end
313
+
245
314
  rv.configure self, params
315
+
246
316
  if block_given?
247
317
  old_view_parent, @view_parent = @view_parent, rv
248
318
  yield
@@ -252,9 +322,11 @@ def ruboto_import_widget(class_name, package_name="android.widget")
252
322
  end
253
323
  "
254
324
 
255
- setup_list_view if class_name == :ListView
256
- setup_button if class_name == :Button
257
- setup_linear_layout if class_name == :LinearLayout
325
+ setup_list_view if class_name == :ListView
326
+ setup_spinner if class_name == :Spinner
327
+ setup_button if class_name == :Button
328
+ setup_image_button if class_name == :ImageButton
329
+ setup_linear_layout if class_name == :LinearLayout
258
330
  setup_relative_layout if class_name == :RelativeLayout
259
331
  end
260
332
 
@@ -264,52 +336,52 @@ end
264
336
  #
265
337
 
266
338
  def setup_view
267
- java_import "android.view.View"
268
- java_import "android.view.ViewGroup"
339
+ java_import "android.view.View"
340
+ java_import "android.view.ViewGroup"
269
341
 
270
- View.class_eval do
271
- @@convert_constants ||= {}
342
+ View.class_eval do
343
+ @@convert_constants ||= {}
272
344
 
273
- def self.add_constant_conversion(from, to)
274
- @@convert_constants[from] = to
275
- end
276
-
277
- def self.convert_constant(from)
278
- @@convert_constants[from] or from
279
- end
280
-
281
- def self.setup_constant_conversion
282
- (self.constants - self.superclass.constants).each do |i|
283
- View.add_constant_conversion i.downcase.to_sym, self.const_get(i)
345
+ def self.add_constant_conversion(from, to)
346
+ @@convert_constants[from] = to
284
347
  end
285
- end
286
348
 
287
- def configure(context, params = {})
288
- if width = params.delete(:width)
289
- getLayoutParams.width = View.convert_constant(width)
349
+ def self.convert_constant(from)
350
+ @@convert_constants[from] or from
290
351
  end
291
352
 
292
- if height = params.delete(:height)
293
- getLayoutParams.height = View.convert_constant(height)
353
+ def self.setup_constant_conversion
354
+ (self.constants - self.superclass.constants).each do |i|
355
+ View.add_constant_conversion i.downcase.to_sym, self.const_get(i)
356
+ end
294
357
  end
295
358
 
296
- if layout = params.delete(:layout)
297
- lp = getLayoutParams
298
- layout.each do |k, v|
299
- values = (v.is_a?(Array) ? v : [v]).map{|i| @@convert_constants[i] or i}
300
- lp.send("#{k.to_s.gsub(/_([a-z])/) {$1.upcase}}", *values)
359
+ def configure(context, params = {})
360
+ if width = params.delete(:width)
361
+ getLayoutParams.width = View.convert_constant(width)
362
+ end
363
+
364
+ if height = params.delete(:height)
365
+ getLayoutParams.height = View.convert_constant(height)
366
+ end
367
+
368
+ if layout = params.delete(:layout)
369
+ lp = getLayoutParams
370
+ layout.each do |k, v|
371
+ values = (v.is_a?(Array) ? v : [v]).map { |i| @@convert_constants[i] or i }
372
+ lp.send("#{k.to_s.gsub(/_([a-z])/) { $1.upcase }}", *values)
373
+ end
301
374
  end
302
- end
303
375
 
304
- params.each do |k, v|
305
- values = (v.is_a?(Array) ? v : [v]).map{|i| @@convert_constants[i] or i}
306
- self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) {$2.upcase}}", *values)
376
+ params.each do |k, v|
377
+ values = (v.is_a?(Array) ? v : [v]).map { |i| @@convert_constants[i] or i }
378
+ self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", *values)
379
+ end
307
380
  end
308
381
  end
309
- end
310
382
 
311
- View.add_constant_conversion :wrap_content, ViewGroup::LayoutParams::WRAP_CONTENT
312
- View.add_constant_conversion :fill_parent, ViewGroup::LayoutParams::FILL_PARENT
383
+ View.add_constant_conversion :wrap_content, ViewGroup::LayoutParams::WRAP_CONTENT
384
+ View.add_constant_conversion :fill_parent, ViewGroup::LayoutParams::FILL_PARENT
313
385
  end
314
386
 
315
387
  #############################################################################
@@ -336,6 +408,17 @@ def setup_button
336
408
  ruboto_register_handler("org.ruboto.callbacks.RubotoOnClickListener", "click", Button, "setOnClickListener")
337
409
  end
338
410
 
411
+ def setup_image_button
412
+ ImageButton.class_eval do
413
+ def configure(context, params = {})
414
+ setOnClickListener(context)
415
+ super(context, params)
416
+ end
417
+ end
418
+
419
+ ruboto_register_handler("org.ruboto.callbacks.RubotoOnClickListener", "click", ImageButton, "setOnClickListener")
420
+ end
421
+
339
422
  def setup_list_view
340
423
  ListView.class_eval do
341
424
  attr_reader :adapter, :adapter_list
@@ -344,16 +427,20 @@ def setup_list_view
344
427
  if params.has_key? :list
345
428
  @adapter_list = Java::java.util.ArrayList.new
346
429
  @adapter_list.addAll(params[:list])
347
- @adapter = Java::android.widget.ArrayAdapter.new(context, R::layout::simple_list_item_1, @adapter_list)
430
+ item_layout = params.delete(:item_layout) || R::layout::simple_list_item_1
431
+ @adapter = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list)
348
432
  setAdapter @adapter
349
433
  params.delete :list
350
434
  end
435
+ if params.has_key? :adapter
436
+ @adapter = params[:adapter]
437
+ end
351
438
  setOnItemClickListener(context)
352
439
  super(context, params)
353
440
  end
354
441
 
355
442
  def reload_list(list)
356
- @adapter_list.clear();
443
+ @adapter_list.clear
357
444
  @adapter_list.addAll(list)
358
445
  @adapter.notifyDataSetChanged
359
446
  invalidate
@@ -363,6 +450,35 @@ def setup_list_view
363
450
  ruboto_register_handler("org.ruboto.callbacks.RubotoOnItemClickListener", "item_click", ListView, "setOnItemClickListener")
364
451
  end
365
452
 
453
+ def setup_spinner
454
+ Spinner.class_eval do
455
+ attr_reader :adapter, :adapter_list
456
+
457
+ def configure(context, params = {})
458
+ if params.has_key? :list
459
+ @adapter_list = Java::java.util.ArrayList.new
460
+ @adapter_list.addAll(params[:list])
461
+ item_layout = params.delete(:item_layout) || R::layout::simple_spinner_item
462
+ @adapter = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list)
463
+ @adapter.setDropDownViewResource(params.delete(:dropdown_layout) || R::layout::simple_spinner_dropdown_item)
464
+ setAdapter @adapter
465
+ params.delete :list
466
+ end
467
+ setOnItemSelectedListener(context)
468
+ super(context, params)
469
+ end
470
+
471
+ def reload_list(list)
472
+ @adapter.clear
473
+ @adapter.addAll(list)
474
+ @adapter.notifyDataSetChanged
475
+ invalidate
476
+ end
477
+ end
478
+
479
+ ruboto_register_handler("org.ruboto.callbacks.RubotoOnItemSelectedListener", "item_selected", Spinner, "setOnItemSelectedListener")
480
+ end
481
+
366
482
  #############################################################################
367
483
  #
368
484
  # Import a class and set it up for handlers
@@ -378,31 +494,31 @@ end
378
494
  #
379
495
  # Allows RubotoActivity to handle callbacks covering Class based handlers
380
496
  #
381
-
382
497
  def ruboto_register_handler(handler_class, unique_name, for_class, method_name)
383
- klass_name = handler_class[/.+\.([A-Z].+)/,1]
384
- klass = ruboto_import handler_class
385
- return unless klass
386
-
387
- RubotoActivity.class_eval "
388
- attr_accessor :#{unique_name}_handler
498
+ klass_name = handler_class[/.+\.([A-Z].+)/, 1]
499
+ klass = ruboto_import handler_class
389
500
 
390
- def #{unique_name}_handler
391
- @#{unique_name}_handler ||= #{klass_name}.new
392
- end
501
+ unless RubotoActivity.method_defined? "#{unique_name}_handler"
502
+ RubotoActivity.class_eval "
503
+ def #{unique_name}_handler
504
+ @#{unique_name}_handler ||= #{klass_name}.new
505
+ end
393
506
 
394
- def handle_#{unique_name}(&block)
395
- #{unique_name}_handler.handle_#{unique_name} &block
396
- self
397
- end
398
- "
507
+ def handle_#{unique_name}(&block)
508
+ #{unique_name}_handler.handle_#{unique_name} &block
509
+ self
510
+ end
511
+ "
512
+ end
399
513
 
400
- for_class.class_eval "
401
- alias_method :orig_#{method_name}, :#{method_name}
402
- def #{method_name}(handler)
403
- orig_#{method_name}(handler.kind_of?(RubotoActivity) ? handler.#{unique_name}_handler : handler)
404
- end
405
- "
514
+ unless for_class.method_defined? "orig_#{method_name}"
515
+ for_class.class_eval "
516
+ alias_method :orig_#{method_name}, :#{method_name}
517
+ def #{method_name}(handler)
518
+ orig_#{method_name}(handler.kind_of?(RubotoActivity) ? handler.#{unique_name}_handler : handler)
519
+ end
520
+ "
521
+ end
406
522
  end
407
523
 
408
524
  #############################################################################
@@ -411,7 +527,7 @@ end
411
527
  #
412
528
 
413
529
  def ruboto_import_preferences(*preferences)
414
- preferences.each{|i| ruboto_import_preference i}
530
+ preferences.each { |i| ruboto_import_preference i }
415
531
  end
416
532
 
417
533
  def ruboto_import_preference(class_name, package_name="android.preference")
@@ -421,7 +537,7 @@ def ruboto_import_preference(class_name, package_name="android.preference")
421
537
  setup_preferences
422
538
 
423
539
  RubotoPreferenceActivity.class_eval "
424
- def #{(class_name.to_s.gsub(/([A-Z])/) {'_' + $1.downcase})[1..-1]}(params={})
540
+ def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={})
425
541
  rv = #{class_name}.new self
426
542
  rv.configure self, params
427
543
  @parent.addPreference(rv) if @parent
@@ -464,7 +580,7 @@ def setup_preferences
464
580
  def on_create(bundle)
465
581
  @parent = nil
466
582
  setPreferenceScreen(instance_eval &@preference_screen_block) if @preference_screen_block
467
- instance_eval {@finish_create_block.call} if @finish_create_block
583
+ instance_eval { @finish_create_block.call } if @finish_create_block
468
584
  end
469
585
  end
470
586
 
@@ -472,9 +588,9 @@ def setup_preferences
472
588
  def configure(context, params = {})
473
589
  params.each do |k, v|
474
590
  if v.is_a?(Array)
475
- self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) {$2.upcase}}", *v)
591
+ self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", *v)
476
592
  else
477
- self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) {$2.upcase}}", v)
593
+ self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", v)
478
594
  end
479
595
  end
480
596
  end
@@ -488,22 +604,22 @@ end
488
604
  #
489
605
  # Final set up depending on globals
490
606
  #
491
-
492
- if $activity
607
+ # TODO(uwe): I think ruboto.rb should not contain any conditions so it does not matter when it is required.
608
+ # TODO(uwe): Remove the commented lines after checking with Scott.
609
+ # if $activity
493
610
  java_import "org.ruboto.RubotoActivity"
494
611
  setup_activity
495
612
  ruboto_configure_activity(RubotoActivity)
496
613
  ruboto_setup(RubotoActivity)
497
614
  setup_view
498
- end
615
+ # end
499
616
 
500
- if $service
617
+ # if $service
501
618
  java_import "org.ruboto.RubotoService"
502
619
  ruboto_setup(RubotoService)
503
- end
620
+ # end
504
621
 
505
- if $broadcast_receiver
622
+ # if $broadcast_receiver
506
623
  java_import "org.ruboto.RubotoBroadcastReceiver"
507
624
  ruboto_setup(RubotoBroadcastReceiver, "receive")
508
- end
509
-
625
+ # end