ruboto 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/Gemfile.lock +9 -9
  2. data/README.md +2 -0
  3. data/Rakefile +71 -7
  4. data/assets/Rakefile +2 -407
  5. data/assets/libs/dexmaker20120305.jar +0 -0
  6. data/assets/rakelib/ruboto.rake +428 -0
  7. data/assets/samples/sample_broadcast_receiver.rb +7 -3
  8. data/assets/samples/sample_broadcast_receiver_test.rb +47 -1
  9. data/assets/src/RubotoActivity.java +6 -2
  10. data/assets/src/org/ruboto/EntryPointActivity.java +2 -2
  11. data/assets/src/org/ruboto/Script.java +91 -24
  12. data/assets/src/org/ruboto/test/ActivityTest.java +27 -24
  13. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +42 -8
  14. data/assets/src/ruboto/activity.rb +1 -1
  15. data/assets/src/ruboto/base.rb +17 -6
  16. data/assets/src/ruboto/generate.rb +458 -0
  17. data/assets/src/ruboto/legacy.rb +9 -12
  18. data/assets/src/ruboto/widget.rb +9 -1
  19. data/lib/java_class_gen/android_api.xml +1 -1
  20. data/lib/ruboto.rb +1 -2
  21. data/lib/ruboto/commands/base.rb +19 -4
  22. data/lib/ruboto/sdk_versions.rb +12 -0
  23. data/lib/ruboto/util/build.rb +10 -11
  24. data/lib/ruboto/util/update.rb +150 -51
  25. data/lib/ruboto/util/verify.rb +6 -4
  26. data/lib/ruboto/util/xml_element.rb +2 -2
  27. data/lib/ruboto/version.rb +1 -1
  28. data/test/activity/option_menu_activity.rb +5 -1
  29. data/test/activity/psych_activity.rb +11 -6
  30. data/test/activity/stack_activity_test.rb +13 -5
  31. data/test/app_test_methods.rb +4 -3
  32. data/test/broadcast_receiver_test.rb +86 -0
  33. data/test/minimal_app_test.rb +27 -19
  34. data/test/rake_test.rb +13 -2
  35. data/test/ruboto_gen_test.rb +17 -3
  36. data/test/ruboto_update_test.rb +24 -2
  37. data/test/service_test.rb +1 -1
  38. data/test/test_helper.rb +134 -62
  39. data/test/update_test_methods.rb +40 -14
  40. data/test/updated_example_test_methods.rb +41 -0
  41. metadata +12 -6
Binary file
@@ -0,0 +1,428 @@
1
+ if `ant -version` !~ /version (\d+)\.(\d+)\.(\d+)/ || $1.to_i < 1 || ($1.to_i == 1 && $2.to_i < 8)
2
+ puts "ANT version 1.8.0 or later required. Version found: #{$1}.#{$2}.#{$3}"
3
+ exit 1
4
+ end
5
+
6
+ require 'time'
7
+
8
+ def manifest() @manifest ||= REXML::Document.new(File.read(MANIFEST_FILE)) end
9
+ def package() manifest.root.attribute('package') end
10
+ def build_project_name() @build_project_name ||= REXML::Document.new(File.read('build.xml')).elements['project'].attribute(:name).value end
11
+ def scripts_path() @sdcard_path ||= "/mnt/sdcard/Android/data/#{package}/files/scripts" end
12
+ def app_files_path() @app_files_path ||= "/data/data/#{package}/files" end
13
+
14
+ require 'rake/clean'
15
+ require 'rexml/document'
16
+
17
+ PROJECT_DIR = File.expand_path('..', File.dirname(__FILE__))
18
+ UPDATE_MARKER_FILE = File.join(PROJECT_DIR, 'bin', 'LAST_UPDATE')
19
+ BUNDLE_JAR = File.expand_path 'libs/bundle.jar'
20
+ BUNDLE_PATH = File.expand_path 'bin/bundle'
21
+ MANIFEST_FILE = File.expand_path 'AndroidManifest.xml'
22
+ RUBOTO_CONFIG_FILE = File.expand_path 'ruboto.yml'
23
+ GEM_FILE = File.expand_path('Gemfile.apk')
24
+ GEM_LOCK_FILE = File.expand_path('Gemfile.apk.lock')
25
+ RELEASE_APK_FILE = File.expand_path "bin/#{build_project_name}-release.apk"
26
+ APK_FILE = File.expand_path "bin/#{build_project_name}-debug.apk"
27
+ TEST_APK_FILE = File.expand_path "test/bin/#{build_project_name}Test-debug.apk"
28
+ JRUBY_JARS = Dir[File.expand_path 'libs/jruby-*.jar']
29
+ RESOURCE_FILES = Dir[File.expand_path 'res/**/*']
30
+ JAVA_SOURCE_FILES = Dir[File.expand_path 'src/**/*.java']
31
+ RUBY_SOURCE_FILES = Dir[File.expand_path 'src/**/*.rb']
32
+ APK_DEPENDENCIES = [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES + RUBY_SOURCE_FILES
33
+ KEYSTORE_FILE = (key_store = File.readlines('ant.properties').grep(/^key.store=/).first) ? File.expand_path(key_store.chomp.sub(/^key.store=/, '').sub('${user.home}', '~')) : "#{build_project_name}.keystore"
34
+ KEYSTORE_ALIAS = (key_alias = File.readlines('ant.properties').grep(/^key.alias=/).first) ? key_alias.chomp.sub(/^key.alias=/, '') : build_project_name
35
+
36
+ CLEAN.include('bin')
37
+
38
+ task :default => :debug
39
+
40
+ file JRUBY_JARS => RUBOTO_CONFIG_FILE do
41
+ next unless File.exists? RUBOTO_CONFIG_FILE
42
+ jruby_jars_mtime = JRUBY_JARS.map { |f| File.mtime(f) }.min
43
+ ruboto_yml_mtime = File.mtime(RUBOTO_CONFIG_FILE)
44
+ next if jruby_jars_mtime > ruboto_yml_mtime
45
+ puts '*' * 80
46
+ if JRUBY_JARS.empty?
47
+ puts ' The JRuby jars are missing.'
48
+ else
49
+ puts " The JRuby jars need reconfiguring after changes to #{RUBOTO_CONFIG_FILE}"
50
+ puts " #{RUBOTO_CONFIG_FILE}: #{ruboto_yml_mtime}"
51
+ puts " #{JRUBY_JARS.join(', ')}: #{jruby_jars_mtime}"
52
+ end
53
+ puts ' Run "ruboto update jruby" to regenerate the JRuby jars'
54
+ puts '*' * 80
55
+ end
56
+
57
+ desc 'build debug package'
58
+ task :debug => APK_FILE
59
+
60
+ namespace :debug do
61
+ desc 'build debug package if compiled files have changed'
62
+ task :quick => [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES do |t|
63
+ build_apk(t, false)
64
+ end
65
+ end
66
+
67
+ desc "build package and install it on the emulator or device"
68
+ task :install => APK_FILE do
69
+ install_apk
70
+ end
71
+
72
+ namespace :install do
73
+ desc 'uninstall, build, and install the application'
74
+ task :clean => [:uninstall, APK_FILE, :install]
75
+
76
+ desc 'Install the application, but only if compiled files are changed.'
77
+ task :quick => 'debug:quick' do
78
+ install_apk
79
+ end
80
+ end
81
+
82
+ desc 'Build APK for release'
83
+ task :release => RELEASE_APK_FILE
84
+
85
+ file RELEASE_APK_FILE => [KEYSTORE_FILE] + APK_DEPENDENCIES do |t|
86
+ build_apk(t, true)
87
+ end
88
+
89
+ desc 'Create a keystore for signing the release APK'
90
+ file KEYSTORE_FILE do
91
+ unless File.read('ant.properties') =~ /^key.store=/
92
+ File.open('ant.properties', 'a'){|f| f << "\nkey.store=#{KEYSTORE_FILE}\n"}
93
+ end
94
+ unless File.read('ant.properties') =~ /^key.alias=/
95
+ File.open('ant.properties', 'a'){|f| f << "\nkey.alias=#{KEYSTORE_ALIAS}\n"}
96
+ end
97
+ sh "keytool -genkey -v -keystore #{KEYSTORE_FILE} -alias #{KEYSTORE_ALIAS} -keyalg RSA -keysize 2048 -validity 10000"
98
+ end
99
+
100
+ desc 'Tag this working copy with the current version'
101
+ task :tag => :release do
102
+ unless `git branch` =~ /^\* master$/
103
+ puts "You must be on the master branch to release!"
104
+ exit!
105
+ end
106
+ # sh "git commit --allow-empty -a -m 'Release #{version}'"
107
+ output = `git status --porcelain`
108
+ raise "Workspace not clean!\n#{output}" unless output.empty?
109
+ sh "git tag #{version}"
110
+ sh "git push origin master --tags"
111
+ end
112
+
113
+ task :sign => :release do
114
+ sh "jarsigner -keystore #{ENV['RUBOTO_KEYSTORE']} -signedjar bin/#{build_project_name}.apk bin/#{build_project_name}-unsigned.apk #{ENV['RUBOTO_KEY_ALIAS']}"
115
+ end
116
+
117
+ task :align => :sign do
118
+ sh "zipalign 4 bin/#{build_project_name}.apk #{build_project_name}.apk"
119
+ end
120
+
121
+ task :publish => :align do
122
+ puts "#{build_project_name}.apk is ready for the market!"
123
+ end
124
+
125
+ desc 'Start the emulator with larger disk'
126
+ task :emulator do
127
+ sh 'emulator -partition-size 1024 -avd Android_3.0'
128
+ end
129
+
130
+ task :start do
131
+ start_app
132
+ end
133
+
134
+ task :stop do
135
+ raise "Unable to stop app. Only available on emulator." unless stop_app
136
+ end
137
+
138
+ desc 'Restart the application'
139
+ task :restart => [:stop, :start]
140
+
141
+ task :uninstall do
142
+ uninstall_apk
143
+ end
144
+
145
+ file MANIFEST_FILE
146
+ file RUBOTO_CONFIG_FILE
147
+
148
+ file APK_FILE => APK_DEPENDENCIES do |t|
149
+ build_apk(t, false)
150
+ end
151
+
152
+ desc 'Copy scripts to emulator or device'
153
+ task :update_scripts => ['install:quick'] do
154
+ update_scripts
155
+ end
156
+
157
+ namespace :update_scripts do
158
+ desc 'Copy scripts to emulator and restart the app'
159
+ task :restart => APK_DEPENDENCIES do |t|
160
+ if build_apk(t, false) || !stop_app
161
+ install_apk
162
+ else
163
+ update_scripts
164
+ end
165
+ start_app
166
+ end
167
+ end
168
+
169
+ task :test => :uninstall do
170
+ Dir.chdir('test') do
171
+ puts 'Running tests'
172
+ sh "adb uninstall #{package}.tests"
173
+ sh "ant instrument install test"
174
+ end
175
+ end
176
+
177
+ namespace :test do
178
+ task :quick => :update_scripts do
179
+ Dir.chdir('test') do
180
+ puts 'Running quick tests'
181
+ sh 'ant instrument'
182
+ sh 'ant installi'
183
+ sh "ant run-tests-quick"
184
+ end
185
+ end
186
+ end
187
+
188
+ file GEM_FILE
189
+ file GEM_LOCK_FILE
190
+
191
+ desc 'Generate bundle jar from Gemfile'
192
+ task :bundle => BUNDLE_JAR
193
+
194
+ file BUNDLE_JAR => [GEM_FILE, GEM_LOCK_FILE] do
195
+ next unless File.exists? GEM_FILE
196
+ puts "Generating #{BUNDLE_JAR}"
197
+
198
+ FileUtils.mkdir_p BUNDLE_PATH
199
+ sh "bundle install --gemfile #{GEM_FILE} --path=#{BUNDLE_PATH}"
200
+ gem_path = Dir["#{BUNDLE_PATH}/*ruby/1.8/gems"][0]
201
+
202
+ if package != 'org.ruboto.core' && JRUBY_JARS.none? { |f| File.exists? f }
203
+ Dir.chdir gem_path do
204
+ Dir['{activerecord-jdbc-adapter, jruby-openssl}-*'].each do |g|
205
+ puts "Removing #{g} gem since it is included in the RubotoCore platform apk."
206
+ FileUtils.rm_rf g
207
+ end
208
+ end
209
+ else
210
+ Dir.chdir gem_path do
211
+ Dir['jruby-openssl-*/lib'].each do |g|
212
+ rel_dir = "#{g}/lib/ruby"
213
+ unless File.exists? rel_dir
214
+ puts "Relocating #{g} files to match standard load path."
215
+ dirs = Dir["#{g}/*"]
216
+ FileUtils.mkdir_p rel_dir
217
+ dirs.each do |d|
218
+ FileUtils.move d, rel_dir
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ # Remove duplicate files
226
+ Dir.chdir gem_path do
227
+ scanned_files = []
228
+ source_files = RUBY_SOURCE_FILES.map { |f| f.gsub("#{PROJECT_DIR}/src/", '') }
229
+ Dir["*/lib/**/*"].each do |f|
230
+ next if File.directory? f
231
+ raise "Malformed file name" unless f =~ %r{^(.*?)/lib/(.*)$}
232
+ gem_name, lib_file = $1, $2
233
+ if existing_file = scanned_files.find { |sf| sf =~ %r{(.*?)/lib/#{lib_file}} }
234
+ puts "Overwriting duplicate file #{lib_file} in gem #{$1} with file in #{gem_name}"
235
+ FileUtils.rm existing_file
236
+ scanned_files.delete existing_file
237
+ elsif source_files.include? lib_file
238
+ puts "Removing duplicate file #{lib_file} in gem #{gem_name}"
239
+ puts "Already present in project source src/#{lib_file}"
240
+ FileUtils.rm f
241
+ next
242
+ end
243
+ scanned_files << f
244
+ end
245
+ end
246
+
247
+ # Expand JARs
248
+ Dir.chdir gem_path do
249
+ Dir['*'].each do |gem_lib|
250
+ Dir.chdir "#{gem_lib}/lib" do
251
+ Dir['**/*.jar'].each do |jar|
252
+ if jar == 'arjdbc/jdbc/adapter_java.jar'
253
+ jar_load_code = <<-END_CODE
254
+ require 'jruby'
255
+ Java::arjdbc.jdbc.AdapterJavaService.new.basicLoad(JRuby.runtime)
256
+ END_CODE
257
+ elsif jar =~ /shared\/jopenssl.jar$/
258
+ jar_load_code = <<-END_CODE
259
+ require 'jruby'
260
+ puts 'Starting JRuby OpenSSL Service'
261
+ public
262
+ Java::JopensslService.new.basicLoad(JRuby.runtime)
263
+ END_CODE
264
+ else
265
+ jar_load_code = ''
266
+ end
267
+ unless jar =~ /sqlite-jdbc/
268
+ puts "Expanding #{gem_lib} #{jar} into #{BUNDLE_JAR}"
269
+ `jar xf #{jar}`
270
+ end
271
+ puts "Writing dummy JAR file #{jar + '.rb'}"
272
+ File.open(jar + '.rb', 'w') { |f| f << jar_load_code }
273
+ if jar.end_with?('.jar')
274
+ puts "Writing dummy JAR file #{jar.sub(/.jar$/, '.rb')}"
275
+ File.open(jar.sub(/.jar$/, '.rb'), 'w') { |f| f << jar_load_code }
276
+ end
277
+ FileUtils.rm_f(jar)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+
284
+ FileUtils.rm_f BUNDLE_JAR
285
+ Dir["#{gem_path}/*"].each_with_index do |gem_dir, i|
286
+ `jar #{i == 0 ? 'c' : 'u'}f #{BUNDLE_JAR} -C #{gem_dir}/lib .`
287
+ end
288
+ FileUtils.rm_rf BUNDLE_PATH
289
+ end
290
+
291
+ # Methods
292
+
293
+ def mark_update(time = Time.now)
294
+ FileUtils.mkdir_p File.dirname(UPDATE_MARKER_FILE)
295
+ File.open(UPDATE_MARKER_FILE, 'w') { |f| f << time.iso8601 }
296
+ end
297
+
298
+ def clear_update
299
+ mark_update File.ctime APK_FILE
300
+ if device_path_exists?(scripts_path)
301
+ sh "adb shell rm -r #{scripts_path}"
302
+ puts "Deleted scripts directory #{scripts_path}"
303
+ end
304
+ end
305
+
306
+ def strings(name)
307
+ @strings ||= REXML::Document.new(File.read('res/values/strings.xml'))
308
+ value = @strings.elements["//string[@name='#{name.to_s}']"] or raise "string '#{name}' not found in strings.xml"
309
+ value.text
310
+ end
311
+
312
+ def version()
313
+ strings :version_name
314
+ end
315
+
316
+ def app_name()
317
+ strings :app_name
318
+ end
319
+
320
+ def main_activity()
321
+ manifest.root.elements['application'].elements["activity[@android:label='@string/app_name']"].attribute('android:name')
322
+ end
323
+
324
+ def device_path_exists?(path)
325
+ path_output =`adb shell ls #{path}`
326
+ result = path_output.chomp !~ /No such file or directory|opendir failed, Permission denied/
327
+ result
328
+ end
329
+
330
+ def package_installed? test = false
331
+ package_name = "#{package}#{'.tests' if test}"
332
+ ['', '-0', '-1', '-2'].each do |i|
333
+ p = "/data/app/#{package_name}#{i}.apk"
334
+ o = `adb shell ls -l #{p}`.chomp
335
+ if o =~ /^-rw-r--r-- system\s+system\s+(\d+) \d{4}-\d{2}-\d{2} \d{2}:\d{2} #{File.basename(p)}$/
336
+ apk_file = test ? TEST_APK_FILE : APK_FILE
337
+ if !File.exists?(apk_file) || $1.to_i == File.size(apk_file)
338
+ return true
339
+ else
340
+ return false
341
+ end
342
+ end
343
+ end
344
+ return nil
345
+ end
346
+
347
+ private
348
+
349
+ def replace_faulty_code(faulty_file, faulty_code)
350
+ explicit_requires = Dir["#{faulty_file.chomp('.rb')}/*.rb"].sort.map { |f| File.basename(f) }.map do |filename|
351
+ "require 'active_model/validations/#{filename}'"
352
+ end.join("\n")
353
+
354
+ old_code = File.read(faulty_file)
355
+ new_code = old_code.gsub faulty_code, explicit_requires
356
+ if new_code != old_code
357
+ puts "Replaced directory listing code in file #{faulty_file} with explicit requires."
358
+ File.open(faulty_file, 'w') { |f| f << new_code }
359
+ else
360
+ puts "Could not find expected faulty code\n\n#{faulty_code}\n\nin file #{faulty_file}\n\n#{old_code}\n\n"
361
+ end
362
+ end
363
+
364
+ def build_apk(t, release)
365
+ apk_file = release ? RELEASE_APK_FILE : APK_FILE
366
+ if File.exist?(apk_file)
367
+ changed_prereqs = t.prerequisites.select do |p|
368
+ File.file?(p) && !Dir[p].empty? && Dir[p].map { |f| File.mtime(f) }.max > File.mtime(APK_FILE)
369
+ end
370
+ return false if changed_prereqs.empty?
371
+ changed_prereqs.each { |f| puts "#{f} changed." }
372
+ puts "Forcing rebuild of #{apk_file}."
373
+ end
374
+ if release
375
+ sh 'ant release'
376
+ else
377
+ sh 'ant debug'
378
+ end
379
+ return true
380
+ end
381
+
382
+ def install_apk
383
+ case package_installed?
384
+ when true
385
+ puts "Package #{package} already installed."
386
+ return
387
+ when false
388
+ puts "Package installed, but of wrong size."
389
+ end
390
+ sh 'ant installd'
391
+ clear_update
392
+ end
393
+
394
+ def uninstall_apk
395
+ return if package_installed?.nil?
396
+ puts "Uninstalling package #{package}"
397
+ system "adb uninstall #{package}"
398
+ if $? != 0 && package_installed?
399
+ puts "Uninstall failed exit code #{$?}"
400
+ exit $?
401
+ end
402
+ end
403
+
404
+ def update_scripts
405
+ `adb shell mkdir -p #{scripts_path}` if !device_path_exists?(scripts_path)
406
+ puts "Pushing files to apk public file area."
407
+ last_update = File.exists?(UPDATE_MARKER_FILE) ? Time.parse(File.read(UPDATE_MARKER_FILE)) : Time.parse('1970-01-01T00:00:00')
408
+ # TODO(uwe): Use `adb sync src` instead?
409
+ Dir.chdir('src') do
410
+ Dir["**/*.rb"].each do |script_file|
411
+ next if File.directory? script_file
412
+ next if File.mtime(script_file) < last_update
413
+ next if script_file =~ /~$/
414
+ print "#{script_file}: "; $stdout.flush
415
+ `adb push #{script_file} #{scripts_path}/#{script_file}`
416
+ end
417
+ end
418
+ mark_update
419
+ end
420
+
421
+ def start_app
422
+ `adb shell am start -a android.intent.action.MAIN -n #{package}/.#{main_activity}`
423
+ end
424
+
425
+ def stop_app
426
+ output = `adb shell ps | grep #{package} | awk '{print $2}' | xargs adb shell kill`
427
+ return output !~ /Operation not permitted/
428
+ end
@@ -1,9 +1,13 @@
1
1
  require 'ruboto/broadcast_receiver'
2
2
 
3
- # will get called whenever the BroadcastReceiver receives an intent (whenever onReceive is called)
3
+ import android.util.Log
4
+
4
5
  RubotoBroadcastReceiver.new_with_callbacks do
6
+ # will get called whenever the BroadcastReceiver receives an intent (whenever onReceive is called)
5
7
  def on_receive(context, intent)
6
- Log.v "MYAPP", intent.getExtras.to_s
8
+ Log.v "SampleBroadcastReceiver", 'Broadcast received!'
9
+ Log.v "SampleBroadcastReceiver", intent.getExtras.to_s
10
+ context.run_on_ui_thread{$activity.title = 'Broadcast received!'}
11
+ Log.v "SampleBroadcastReceiver", 'Broadcast processed OK!'
7
12
  end
8
13
  end
9
-
@@ -1 +1,47 @@
1
- # TODO
1
+ # Change this to the activity that will send the broadcast
2
+ activity Java::THE_PACKAGE.SampleActivity
3
+
4
+ # Change this to wait for the activity to be created
5
+ setup do |activity|
6
+ start = Time.now
7
+ loop do
8
+ @text_view = activity.findViewById(42)
9
+ break if @text_view || (Time.now - start > 60)
10
+ sleep 1
11
+ end
12
+ assert @text_view
13
+ end
14
+
15
+ # Change this to trigger the sending of broadcast intents
16
+ # and assert that the receiver behaves correctly.
17
+ test('broadcast changes title', :ui => false) do |activity|
18
+ begin
19
+ @receiver = $package.SampleBroadcastReceiver.new
20
+ action = '__THE_PACKAGE__.SampleBroadcastReceiver.action'
21
+ filter = android.content.IntentFilter.new(action)
22
+ receiver_ready = false
23
+ Thread.start do
24
+ begin
25
+ android.os.Looper.prepare
26
+ activity.registerReceiver(@receiver, filter, nil, android.os.Handler.new)
27
+ receiver_ready = true
28
+ android.os.Looper.loop
29
+ rescue
30
+ puts "Exception starting receiver"
31
+ puts $!.message
32
+ puts $!.backtrace.join("\n")
33
+ end
34
+ end
35
+ sleep 0.1 until receiver_ready
36
+ intent = android.content.Intent.new
37
+ intent.set_action action
38
+ activity.send_broadcast(intent)
39
+ bc_sent_at = Time.now
40
+
41
+ message = 'Broadcast received!'
42
+ sleep 0.1 until activity.title == message || (Time.now - bc_sent_at) > 10
43
+ assert_equal message, activity.title
44
+ ensure
45
+ activity.unregister_receiver(@receiver) if @receiver
46
+ end
47
+ end