Permalink
Browse files

* Issue #6 Port to Ruboto Core

* Did 'ruboto update app' and tweaked tests to run green
  • Loading branch information...
1 parent 4a8174b commit 143924272939a8fcc7bfabdbfe8be19d7ae6bc92 @donv donv committed Jan 1, 2012
Showing with 2,229 additions and 623 deletions.
  1. +2 −0 .gitignore
  2. +59 −79 AndroidManifest.xml
  3. +340 −108 Rakefile
  4. +18 −0 ant.properties
  5. +0 −15 build.properties
  6. +70 −55 build.xml
  7. BIN libs/{jruby-core-1.6.4.jar → jruby-core-1.7.0.dev.jar}
  8. BIN libs/{jruby-stdlib-1.6.4.jar → jruby-stdlib-1.7.0.dev.jar}
  9. +40 −0 proguard.cfg
  10. +5 −5 default.properties → project.properties
  11. +25 −0 res/layout/get_ruboto_core.xml
  12. +1 −1 res/values/strings.xml
  13. +194 −0 src/org/ruboto/EntryPointActivity.java
  14. +3 −2 src/org/ruboto/RubotoActivity.java
  15. +25 −15 src/org/ruboto/RubotoBroadcastReceiver.java
  16. +6 −0 src/org/ruboto/RubotoService.java
  17. +85 −57 src/org/ruboto/Script.java
  18. +1 −1 src/org/ruboto/irb/IRBScript.java
  19. +1 −181 src/org/ruboto/irb/ScriptLaunch.java
  20. +24 −0 src/ruboto.rb
  21. +84 −0 src/ruboto/activity.rb
  22. +88 −0 src/ruboto/base.rb
  23. +31 −0 src/ruboto/broadcast_receiver.rb
  24. +223 −0 src/ruboto/legacy.rb
  25. +89 −0 src/ruboto/menu.rb
  26. +78 −0 src/ruboto/preference.rb
  27. +74 −0 src/ruboto/service.rb
  28. +34 −0 src/ruboto/util/stack.rb
  29. +18 −0 src/ruboto/util/toast.rb
  30. +3 −0 src/ruboto/version.rb
  31. +188 −0 src/ruboto/widget.rb
  32. +6 −11 {tests → test}/AndroidManifest.xml
  33. +21 −0 test/ant.properties
  34. +130 −0 test/build.xml
  35. +4 −1 {tests → test}/local.properties
  36. +40 −0 test/proguard.cfg
  37. +4 −4 tests/default.properties → test/project.properties
  38. BIN test/res/drawable-hdpi/icon.png
  39. BIN test/res/drawable-ldpi/icon.png
  40. BIN test/res/drawable-mdpi/icon.png
  41. +1 −0 {tests → test}/src/org/jruby/IRBTest.java
  42. +69 −0 test/src/org/ruboto/test/ActivityTest.java
  43. +127 −0 test/src/org/ruboto/test/InstrumentationTestRunner.java
  44. +1 −1 test/{assets/scripts → src}/script_launch_test.rb
  45. +17 −0 test/src/test_helper.rb
  46. +0 −15 tests/build.properties
  47. +0 −72 tests/build.xml
View
2 .gitignore
@@ -7,3 +7,5 @@ local.properties
generated_libs
.rvmrc
.DS_Store
+.idea
+test/coverage.em
View
138 AndroidManifest.xml
@@ -1,85 +1,65 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.ruboto.irb"
- android:versionCode="9"
- android:versionName="@string/version_name"
- android:installLocation="preferExternal">
-
- <application android:icon="@drawable/icon"
- android:label="@string/app_name"
- android:largeHeap="true"
- android:hardwareAccelerated="true">
- <activity android:name="IRB"
- android:label="@string/app_name"
- android:windowSoftInputMode="adjustResize">
+<?xml version='1.0' encoding='UTF-8'?>
+<manifest xmlns:android='http://schemas.android.com/apk/res/android' android:versionName='@string/version_name' package='org.ruboto.irb' android:versionCode='9' android:installLocation='preferExternal'>
+ <application android:hardwareAccelerated='true' android:label='@string/app_name' android:icon='@drawable/icon' android:largeHeap='true'>
+ <activity android:name='IRB' android:label='@string/app_name' android:windowSoftInputMode='adjustResize'>
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name='android.intent.action.MAIN'/>
+ <category android:name='android.intent.category.LAUNCHER'/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name='android.intent.action.VIEW'/>
+ <category android:name='android.intent.category.DEFAULT'/>
+ <category android:name='android.intent.category.BROWSABLE'/>
+ <data android:scheme='http'/>
+ <data android:host='*'/>
+ <data android:pathPattern='.*\\.rb'/>
</intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="http" />
- <data android:host="*" />
- <data android:pathPattern=".*\\.rb" />
- </intent-filter>
</activity>
- <activity android:name="ShortcutBuilder">
- <intent-filter>
- <action android:name="android.intent.action.CREATE_SHORTCUT" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
+ <activity android:name='ShortcutBuilder'>
+ <intent-filter>
+ <action android:name='android.intent.action.CREATE_SHORTCUT'/>
+ <category android:name='android.intent.category.DEFAULT'/>
+ </intent-filter>
</activity>
- <activity android:name="ScriptLaunch">
- <intent-filter>
- <action android:name="org.ruboto.intent.action.LAUNCH_SCRIPT" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
+ <activity android:name='ScriptLaunch'>
+ <intent-filter>
+ <action android:name='org.ruboto.intent.action.LAUNCH_SCRIPT'/>
+ <category android:name='android.intent.category.DEFAULT'/>
+ </intent-filter>
</activity>
- <activity android:name="org.ruboto.RubotoActivity" android:configChanges="orientation" />
- <activity android:name="org.ruboto.RubotoDialog" android:theme="@android:style/Theme.Dialog" android:configChanges="orientation" />
- <activity android:name="org.ruboto.RubotoPreferenceActivity" />
-
- <service android:name="org.ruboto.RubotoService" android:exported="false" />
+ <activity android:name='org.ruboto.RubotoActivity' android:configChanges='orientation'/>
+ <activity android:name='org.ruboto.RubotoDialog' android:theme='@android:style/Theme.Dialog' android:configChanges='orientation'/>
+ <activity android:name='org.ruboto.RubotoPreferenceActivity'/>
+ <service android:name='org.ruboto.RubotoService' android:exported='false'/>
</application>
-
- <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="13"/>
-
- <supports-screens
- android:xlargeScreens="true"
- android:largeScreens="true"
- android:normalScreens="true"
- android:smallScreens="true"
- android:anyDensity="true"/>
-
- <uses-feature android:name="android.hardware.telephony" android:required="false" />
- <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
- <uses-feature android:name="android.hardware.location" android:required="false" />
- <uses-feature android:name="android.hardware.location.gps" android:required="false" />
- <uses-feature android:name="android.hardware.location.network" android:required="false" />
- <uses-feature android:name="android.hardware.wifi" android:required="false" />
- <uses-feature android:name="android.hardware.microphone" android:required="false" />
- <uses-feature android:name="android.hardware.camera" android:required="false" />
- <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
- <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
-
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.CALL_PHONE"/>
- <uses-permission android:name="android.permission.SEND_SMS"/>
- <uses-permission android:name="android.permission.VIBRATE"/>
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.PERSISTENT_ACTIVITY"/>
- <uses-permission android:name="android.permission.RESTART_PACKAGES"/>
- <uses-permission android:name="android.permission.GET_TASKS"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <uses-permission android:name="android.permission.CAMERA"/>
- <uses-permission android:name="android.permission.BLUETOOTH"/>
-</manifest>
+ <uses-sdk android:minSdkVersion='5' android:targetSdkVersion='13'/>
+ <supports-screens android:smallScreens='true' android:normalScreens='true' android:largeScreens='true' android:xlargeScreens='true' android:anyDensity='true'/>
+ <uses-feature android:required='false' android:name='android.hardware.telephony'/>
+ <uses-feature android:required='false' android:name='android.hardware.touchscreen'/>
+ <uses-feature android:required='false' android:name='android.hardware.location'/>
+ <uses-feature android:required='false' android:name='android.hardware.location.gps'/>
+ <uses-feature android:required='false' android:name='android.hardware.location.network'/>
+ <uses-feature android:required='false' android:name='android.hardware.wifi'/>
+ <uses-feature android:required='false' android:name='android.hardware.microphone'/>
+ <uses-feature android:required='false' android:name='android.hardware.camera'/>
+ <uses-feature android:required='false' android:name='android.hardware.camera.autofocus'/>
+ <uses-feature android:required='false' android:name='android.hardware.bluetooth'/>
+ <uses-permission android:name='android.permission.WRITE_EXTERNAL_STORAGE'/>
+ <uses-permission android:name='com.android.launcher.permission.INSTALL_SHORTCUT'/>
+ <uses-permission android:name='android.permission.INTERNET'/>
+ <uses-permission android:name='android.permission.ACCESS_FINE_LOCATION'/>
+ <uses-permission android:name='android.permission.ACCESS_COARSE_LOCATION'/>
+ <uses-permission android:name='android.permission.CALL_PHONE'/>
+ <uses-permission android:name='android.permission.SEND_SMS'/>
+ <uses-permission android:name='android.permission.VIBRATE'/>
+ <uses-permission android:name='android.permission.ACCESS_WIFI_STATE'/>
+ <uses-permission android:name='android.permission.CHANGE_WIFI_STATE'/>
+ <uses-permission android:name='android.permission.READ_PHONE_STATE'/>
+ <uses-permission android:name='android.permission.PERSISTENT_ACTIVITY'/>
+ <uses-permission android:name='android.permission.RESTART_PACKAGES'/>
+ <uses-permission android:name='android.permission.GET_TASKS'/>
+ <uses-permission android:name='android.permission.ACCESS_NETWORK_STATE'/>
+ <uses-permission android:name='android.permission.RECORD_AUDIO'/>
+ <uses-permission android:name='android.permission.CAMERA'/>
+ <uses-permission android:name='android.permission.BLUETOOTH'/>
+</manifest>
View
448 Rakefile
@@ -1,6 +1,87 @@
+if `ant -version` !~ /version (\d+)\.(\d+)\.(\d+)/ || $1.to_i < 1 || ($1.to_i == 1 && $2.to_i < 8)
+ puts "ANT version 1.8.1 or later required. Version found: #{$1}.#{$2}.#{$3}"
+ exit 1
+end
+
+require 'time'
+
+def manifest() @manifest ||= REXML::Document.new(File.read(MANIFEST_FILE)) end
+def package() manifest.root.attribute('package') end
+def build_project_name() @build_project_name ||= REXML::Document.new(File.read('build.xml')).elements['project'].attribute(:name).value end
+def scripts_path() @sdcard_path ||= "/mnt/sdcard/Android/data/#{package}/files/scripts" end
+def app_files_path() @app_files_path ||= "/data/data/#{package}/files" end
+
require 'rake/clean'
+require 'rexml/document'
+
+PROJECT_DIR = Dir.getwd
+UPDATE_MARKER_FILE = File.expand_path(File.join('bin', 'LAST_UPDATE'), File.dirname(__FILE__))
+BUNDLE_JAR = File.expand_path 'libs/bundle.jar'
+BUNDLE_PATH = File.expand_path 'bin/bundle'
+MANIFEST_FILE = File.expand_path 'AndroidManifest.xml'
+RUBOTO_CONFIG_FILE = File.expand_path 'ruboto.yml'
+GEM_FILE = File.expand_path('Gemfile.apk')
+GEM_LOCK_FILE = File.expand_path('Gemfile.apk.lock')
+RELEASE_APK_FILE = File.expand_path "bin/#{build_project_name}-release.apk"
+APK_FILE = File.expand_path "bin/#{build_project_name}-debug.apk"
+TEST_APK_FILE = File.expand_path "test/bin/#{build_project_name}Test-debug.apk"
+JRUBY_JARS = Dir[File.expand_path 'libs/jruby-*.jar']
+RESOURCE_FILES = Dir[File.expand_path 'res/**/*']
+JAVA_SOURCE_FILES = Dir[File.expand_path 'src/**/*.java']
+RUBY_SOURCE_FILES = Dir[File.expand_path 'src/**/*.rb']
+APK_DEPENDENCIES = [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES + RUBY_SOURCE_FILES
+
+CLEAN.include('bin')
+
+task :default => :debug
+
+file JRUBY_JARS => RUBOTO_CONFIG_FILE do
+ next unless File.exists? RUBOTO_CONFIG_FILE
+ jruby_jars_mtime = JRUBY_JARS.map { |f| File.mtime(f) }.min
+ ruboto_yml_mtime = File.mtime(RUBOTO_CONFIG_FILE)
+ next if jruby_jars_mtime > ruboto_yml_mtime
+ puts '*' * 80
+ if JRUBY_JARS.empty?
+ puts ' The JRuby jars are missing.'
+ else
+ puts " The JRuby jars need reconfiguring after changes to #{RUBOTO_CONFIG_FILE}"
+ puts " #{RUBOTO_CONFIG_FILE}: #{ruboto_yml_mtime}"
+ puts " #{JRUBY_JARS.join(', ')}: #{jruby_jars_mtime}"
+ end
+ puts ' Run "ruboto update jruby" to regenerate the JRuby jars'
+ puts '*' * 80
+end
+
+desc 'build debug package'
+task :debug => APK_FILE
+
+namespace :debug do
+ desc 'build debug package if compiled files have changed'
+ task :quick => [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES do |t|
+ build_apk(t, false)
+ end
+end
-CLEAN.include('tmp', 'bin', generated_libs)
+desc "build package and install it on the emulator or device"
+task :install => APK_FILE do
+ install_apk
+end
+
+namespace :install do
+ desc 'uninstall, build, and install the application'
+ task :clean => [:uninstall, APK_FILE, :install]
+
+ desc 'Install the application, but only if compiled files are changed.'
+ task :quick => 'debug:quick' do
+ install_apk
+ end
+end
+
+task :release => RELEASE_APK_FILE
+
+file RELEASE_APK_FILE => APK_DEPENDENCIES do |t|
+ build_apk(t, true)
+end
task :tag => :release do
unless `git branch` =~ /^\* master$/
@@ -10,142 +91,180 @@ task :tag => :release do
sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git tag #{version}"
sh "git push origin master --tags"
+ #sh "gem push pkg/#{name}-#{version}.gem"
end
+task :sign => :release do
+ sh "jarsigner -keystore #{ENV['RUBOTO_KEYSTORE']} -signedjar bin/#{build_project_name}.apk bin/#{build_project_name}-unsigned.apk #{ENV['RUBOTO_KEY_ALIAS']}"
+end
-# These are callbacks that have been remove in case we need them again
-task :removed_callbacks do
- [
- %w(android.opengl.GLSurfaceView.EGLConfigChooser),
- %w(android.opengl.GLSurfaceView.GLWrapper),
-
- %w(android.media.AudioManager.OnAudioFocusChangeListener), #
- %w(android.media.MediaPlayer.OnBufferingUpdateListener MediaPlayerOnBufferingUpdateListener),
- %w(android.media.MediaPlayer.OnCompletionListener MediaPlayerOnCompletionListener),
- %w(android.media.MediaPlayer.OnErrorListener MediaPlayerOnErrorListener),
- %w(android.media.MediaPlayer.OnInfoListener MediaPlayerOnInfoListener),
- %w(android.media.MediaPlayer.OnPreparedListener MediaPlayerOnPreparedListener),
- %w(android.media.MediaPlayer.OnSeekCompleteListener MediaPlayerOnSeekCompleteListener),
- %w(android.media.MediaPlayer.OnVideoSizeChangedListener MediaPlayerOnVideoSizeChangedListener),
- %w(android.media.MediaRecorder.OnErrorListener MediaRecorderOnErrorListener),
- %w(android.media.MediaRecorder.OnInfoListener MediaRecorderOnInfoListener),
- %w(android.media.MediaScannerConnection.OnScanCompletedListener), #
- %w(android.media.SoundPool.OnLoadCompleteListener SoundPoolOnLoadCompleteListener), #
+task :align => :sign do
+ sh "zipalign 4 bin/#{build_project_name}.apk #{build_project_name}.apk"
+end
- %w(android.content.DialogInterface.OnCancelListener DialogOnCancelListener),
- %w(android.content.DialogInterface.OnClickListener DialogOnClickListener),
- %w(android.content.DialogInterface.OnDismissListener DialogOnDismissListener),
- %w(android.content.DialogInterface.OnKeyListener DialogOnKeyListener),
- %w(android.content.DialogInterface.OnMultiChoiceClickListener DialogOnMultiChoiceClickListener),
- %w(android.content.DialogInterface.OnShowListener DialogOnShowListener), #
- %w(android.content.IntentSender.OnFinished IntentSenderOnFinished), #
- %w(android.content.SharedPreferences.OnSharedPreferenceChangeListener),
- %w(android.content.SyncStatusObserver), #
+task :publish => :align do
+ puts "#{build_project_name}.apk is ready for the market!"
+end
- %w(android.location.GpsStatus.Listener GpsStatusListener),
- %w(android.location.GpsStatus.NmeaListener), #
+desc 'Start the emulator with larger disk'
+task :emulator do
+ sh 'emulator -partition-size 1024 -avd Android_3.0'
+end
- %w(android.preference.Preference.OnPreferenceChangeListener),
- %w(android.preference.Preference.OnPreferenceClickListener),
+task :start do
+ start_app
+end
- %w(android.view.View.OnTouchListener),
- %w(android.view.View.OnLongClickListener),
- %w(android.view.View.OnFocusChangeListener),
- %w(android.view.View.OnKeyListener),
+task :stop do
+ raise "Unable to stop app. Only available on emulator." unless stop_app
+end
- %w(android.speech.tts.TextToSpeech.OnInitListener TextToSpeechOnInitListener), #
- %w(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener TextToSpeechOnUtteranceCompletedListener), #
+desc 'Restart the application'
+task :restart => [:stop, :start]
- %w(android.gesture.GestureOverlayView.OnGesturePerformedListener), #
+task :uninstall do
+ uninstall_apk
+end
- %w(android.app.KeyguardManager.OnKeyguardExitResult),
- %w(android.app.PendingIntent.OnFinished PendingIntentOnFinished),
- %w(android.app.SearchManager.OnCancelListener SearchOnCancelListener),
- %w(android.app.SearchManager.OnDismissListener SearchOnDismissListener),
- %w(android.app.DatePickerDialog.OnDateSetListener),
- %w(android.app.TimePickerDialog.OnTimeSetListener),
+file MANIFEST_FILE
+file RUBOTO_CONFIG_FILE
- %w(android.database.sqlite.SQLiteDatabase.CursorFactory SQLiteCursorFactory),
+file APK_FILE => APK_DEPENDENCIES do |t|
+ build_apk(t, false)
+end
- %w(java.lang.Runnable),
- %w(android.os.Handler.Callback Handler),
+desc 'Copy scripts to emulator or device'
+task :update_scripts => ['install:quick'] do
+ update_scripts
+end
- %w(android.widget.AdapterView.OnItemLongClickListener),
- %w(android.widget.TabHost.TabContentFactory),
- %w(android.widget.TabHost.OnTabChangeListener),
- %w(android.widget.TextView.OnEditorActionListener),
- %w(android.widget.DatePicker.OnDateChangedListener),
- %w(android.widget.TimePicker.OnTimeChangedListener),
- ].each do |c, n|
- # Do something
+namespace :update_scripts do
+ desc 'Copy scripts to emulator and restart the app'
+ task :restart => APK_DEPENDENCIES do |t|
+ if stop_app
+ update_scripts
+ else
+ build_apk(t, false)
+ install_apk
+ end
+ start_app
end
end
-# Generate callbacks
-# Make sure to set package to org.ruboto.callbacks
-# TODO: add --package option to gen interface and gen subclass
-task :callbacks do
- [
- %w(android.opengl.GLSurfaceView.EGLContextFactory), #
- %w(android.opengl.GLSurfaceView.EGLWindowSurfaceFactory), #
- %w(android.opengl.GLSurfaceView.Renderer GLSurfaceViewRenderer),
- %w(android.view.SurfaceHolder.Callback SurfaceHolderCallback),
-
-
- %w(android.media.AudioRecord.OnRecordPositionUpdateListener),
- %w(android.media.AudioTrack.OnPlaybackPositionUpdateListener),
- %w(android.media.JetPlayer.OnJetEventListener),
- %w(android.media.MediaScannerConnection.MediaScannerConnectionClient), #
+task :test => :uninstall do
+ Dir.chdir('test') do
+ puts 'Running tests'
+ sh "adb uninstall #{package}.tests"
+ sh "ant instrument install test"
+ end
+end
- %w(android.location.LocationListener),
+namespace :test do
+ task :quick => :update_scripts do
+ Dir.chdir('test') do
+ puts 'Running quick tests'
+ sh 'ant instrument'
+ sh 'ant installi'
+ sh "ant run-tests-quick"
+ end
+ end
+end
- %w(android.view.GestureDetector.OnDoubleTapListener),
- %w(android.view.GestureDetector.OnGestureListener),
- %w(android.view.ScaleGestureDetector.OnScaleGestureListener), #
- %w(android.view.ViewGroup.OnHierarchyChangeListener),
+file GEM_FILE
+file GEM_LOCK_FILE
- %w(android.speech.RecognitionListener), #
+desc 'Generate bundle jar from Gemfile'
+task :bundle => BUNDLE_JAR
- %w(android.gesture.GestureOverlayView.OnGestureListener), #
- %w(android.gesture.GestureOverlayView.OnGesturingListener), #
+file BUNDLE_JAR => [GEM_FILE, GEM_LOCK_FILE] do
+ next unless File.exists? GEM_FILE
+ puts "Generating #{BUNDLE_JAR}"
- %w(android.database.sqlite.SQLiteTransactionListener), #5
+ FileUtils.mkdir_p BUNDLE_PATH
+ sh "bundle install --gemfile #{GEM_FILE} --path=#{BUNDLE_PATH}"
+ gem_path = Dir["#{BUNDLE_PATH}/*ruby/1.8/gems"][0]
- %w(android.widget.AdapterView.OnItemSelectedListener),
+ if package != 'org.ruboto.core' && JRUBY_JARS.none? { |f| File.exists? f }
+ Dir.chdir gem_path do
+ Dir['activerecord-jdbc-adapter-*'].each do |g|
+ puts "Removing #{g} gem since it is included in the RubotoCore platform apk."
+ FileUtils.rm_rf g
+ end
+ end
+ end
- %w(android.hardware.SensorEventListener),
- ].each do |c, n|
- puts `ruboto gen interface #{c} --name Ruboto#{n ? n : c.split(".")[-1]} --force include`
+ # Remove duplicate files
+ Dir.chdir gem_path do
+ scanned_files = []
+ source_files = RUBY_SOURCE_FILES.map { |f| f.gsub("#{PROJECT_DIR}/src/", '') }
+ Dir["*/lib/**/*"].each do |f|
+ next if File.directory? f
+ raise "Malformed file name" unless f =~ %r{^(.*?)/lib/(.*)$}
+ gem_name, lib_file = $1, $2
+ if existing_file = scanned_files.find { |sf| sf =~ %r{(.*?)/lib/#{lib_file}} }
+ puts "Overwriting duplicate file #{lib_file} in gem #{$1} with file in #{gem_name}"
+ FileUtils.rm existing_file
+ scanned_files.delete existing_file
+ elsif source_files.include? lib_file
+ puts "Removing duplicate file #{lib_file} in gem #{gem_name}"
+ puts "Already present in project source src/#{lib_file}"
+ FileUtils.rm f
+ next
+ end
+ scanned_files << f
+ end
end
- [
- %w(android.telephony.PhoneStateListener),
- %w(android.database.sqlite.SQLiteOpenHelper),
- %w(android.view.GestureDetector.SimpleOnGestureListener),
- %w(android.view.ScaleGestureDetector.SimpleOnScaleGestureListener),
- ].each do |c, n|
- puts `ruboto gen subclass #{c} --name Ruboto#{n ? n : c.split(".")[-1]} --method_base on --force include`
+ # Expand JARs
+ Dir.chdir gem_path do
+ Dir['*'].each do |gem_lib|
+ Dir.chdir "#{gem_lib}/lib" do
+ Dir['**/*.jar'].each do |jar|
+ unless jar =~ /sqlite-jdbc/
+ puts "Expanding #{gem_lib} #{jar} into #{BUNDLE_JAR}"
+ `jar xf #{jar}`
+ end
+ if jar == 'arjdbc/jdbc/adapter_java.jar'
+ jar_load_code = <<-END_CODE
+require 'jruby'
+Java::arjdbc.jdbc.AdapterJavaService.new.basicLoad(JRuby.runtime)
+ END_CODE
+ else
+ jar_load_code = ''
+ end
+ puts "Writing dummy JAR file #{jar + '.rb'}"
+ File.open(jar + '.rb', 'w') { |f| f << jar_load_code }
+ if jar.end_with?('.jar')
+ puts "Writing dummy JAR file #{jar.sub(/.jar$/, '.rb')}"
+ File.open(jar.sub(/.jar$/, '.rb'), 'w') { |f| f << jar_load_code }
+ end
+ FileUtils.rm_f(jar)
+ end
+ end
+ end
end
- [
- %w(android.content.ContentProvider),
- ].each do |c, n|
- puts `ruboto gen subclass #{c} --name Ruboto#{n ? n : c.split(".")[-1]} --method_base abstract --force include`
+
+ FileUtils.rm_f BUNDLE_JAR
+ Dir["#{gem_path}/*"].each_with_index do |gem_dir, i|
+ `jar #{i == 0 ? 'c' : 'u'}f #{BUNDLE_JAR} -C #{gem_dir}/lib .`
end
+ FileUtils.rm_rf BUNDLE_PATH
end
-# Generate callback subclasses for widgets
-# Make sure to set package to org.ruboto.widget
-# TODO: add --package option to gen subclass
-task :widgets do
- ruboto_dir = "../ruboto-core/bin/"
- %w(EditText TextView Button ListView ScrollView SeekBar).each do |c, n|
- puts `ruboto gen subclass android.widget.#{c} --name Ruboto#{n ? n : c.split(".")[-1]} --method_base on --force include`
- end
+# Methods
+
+def mark_update(time = Time.now)
+ FileUtils.mkdir_p File.dirname(UPDATE_MARKER_FILE)
+ File.open(UPDATE_MARKER_FILE, 'w') { |f| f << time.iso8601 }
end
-def manifest
- @manifest ||= REXML::Document.new(File.read('AndroidManifest.xml'))
+def clear_update
+ mark_update File.ctime APK_FILE
+ if device_path_exists?(scripts_path)
+ sh "adb shell rm -r #{scripts_path}"
+ puts "Deleted scripts directory #{scripts_path}"
+ end
end
def strings(name)
@@ -154,6 +273,119 @@ def strings(name)
value.text
end
-def package() manifest.root.attribute('package') end
-def version() strings :version_name end
-def app_name() strings :app_name end
+def version()
+ strings :version_name
+end
+
+def app_name()
+ strings :app_name
+end
+
+def main_activity()
+ manifest.root.elements['application'].elements["activity[@android:label='@string/app_name']"].attribute('android:name')
+end
+
+def device_path_exists?(path)
+ path_output =`adb shell ls #{path}`
+ result = path_output.chomp !~ /No such file or directory|opendir failed, Permission denied/
+ result
+end
+
+def package_installed? test = false
+ package_name = "#{package}#{'.tests' if test}"
+ ['', '-0', '-1', '-2'].each do |i|
+ p = "/data/app/#{package_name}#{i}.apk"
+ o = `adb shell ls -l #{p}`.chomp
+ if o =~ /^-rw-r--r-- system\s+system\s+(\d+) \d{4}-\d{2}-\d{2} \d{2}:\d{2} #{File.basename(p)}$/
+ apk_file = test ? TEST_APK_FILE : APK_FILE
+ if !File.exists?(apk_file) || $1.to_i == File.size(apk_file)
+ return true
+ else
+ return false
+ end
+ end
+ end
+ return nil
+end
+
+private
+
+def replace_faulty_code(faulty_file, faulty_code)
+ explicit_requires = Dir["#{faulty_file.chomp('.rb')}/*.rb"].sort.map { |f| File.basename(f) }.map do |filename|
+ "require 'active_model/validations/#{filename}'"
+ end.join("\n")
+
+ old_code = File.read(faulty_file)
+ new_code = old_code.gsub faulty_code, explicit_requires
+ if new_code != old_code
+ puts "Replaced directory listing code in file #{faulty_file} with explicit requires."
+ File.open(faulty_file, 'w') { |f| f << new_code }
+ else
+ puts "Could not find expected faulty code\n\n#{faulty_code}\n\nin file #{faulty_file}\n\n#{old_code}\n\n"
+ end
+end
+
+def build_apk(t, release)
+ apk_file = release ? RELEASE_APK_FILE : APK_FILE
+ if File.exist?(apk_file)
+ changed_prereqs = t.prerequisites.select do |p|
+ File.file?(p) && !Dir[p].empty? && Dir[p].map { |f| File.mtime(f) }.max > File.mtime(APK_FILE)
+ end
+ return if changed_prereqs.empty?
+ changed_prereqs.each { |f| puts "#{f} changed." }
+ puts "Forcing rebuild of #{apk_file}."
+ end
+ if release
+ sh 'ant release'
+ else
+ sh 'ant debug'
+ end
+end
+
+def install_apk
+ case package_installed?
+ when true
+ puts "Package already installed."
+ return
+ when false
+ puts "Package installed, but of wrong size."
+ end
+ sh 'ant installd'
+ clear_update
+end
+
+def uninstall_apk
+ return if package_installed?.nil?
+ puts "Uninstalling package #{package}"
+ system "adb uninstall #{package}"
+ if $? != 0 && package_installed?
+ puts "Uninstall failed exit code #{$?}"
+ exit $?
+ end
+end
+
+def update_scripts
+ `adb shell mkdir -p #{scripts_path}` if !device_path_exists?(scripts_path)
+ puts "Pushing files to apk public file area."
+ last_update = File.exists?(UPDATE_MARKER_FILE) ? Time.parse(File.read(UPDATE_MARKER_FILE)) : Time.parse('1970-01-01T00:00:00')
+ # TODO(uwe): Use `adb sync src` instead?
+ Dir.chdir('src') do
+ Dir["**/*.rb"].each do |script_file|
+ next if File.directory? script_file
+ next if File.mtime(script_file) < last_update
+ next if script_file =~ /~$/
+ print "#{script_file}: "; $stdout.flush
+ `adb push #{script_file} #{scripts_path}/#{script_file}`
+ end
+ end
+ mark_update
+end
+
+def start_app
+ `adb shell am start -a android.intent.action.MAIN -n #{package}/.#{main_activity}`
+end
+
+def stop_app
+ output = `adb shell ps | grep #{package} | awk '{print $2}' | xargs adb shell kill`
+ return output !~ /Operation not permitted/
+end
View
18 ant.properties
@@ -0,0 +1,18 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
+application-package=org.ruboto.irb
View
15 build.properties
@@ -1,15 +0,0 @@
-# This file is used to override default values used by the Ant build system.
-#
-# This file must be checked in Version Control Systems, as it is
-# integral to the build system of your project.
-
-# The name of your application package as defined in the manifest.
-# Used by the 'uninstall' rule.
-application-package=org.ruboto.irb
-
-# The name of the source folder.
-#source-folder=src
-
-# The name of the output folder.
-#out-folder=bin
-
View
125 build.xml
@@ -2,69 +2,84 @@
<project name="IRB" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
- It contain the path to the SDK. It should *NOT* be checked in in Version
- Control Systems. -->
- <property file="local.properties"/>
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
- <!-- The build.properties file can be created by you and is never touched
- by the 'android' tool. This is the place to change some of the default property values
- used by the Ant rules.
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
- application-package
- the name of your application package as defined in the manifest. Used by the
- 'uninstall' rule.
- source-folder
- the name of the source folder. Default is 'src'.
- out-folder
- the name of the output folder. Default is 'bin'.
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
- Properties related to the SDK location or the project target should be updated
- using the 'android' tool with the 'update' action.
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
- This file is an integral part of the build system for your application and
- should be checked in in Version Control Systems.
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
-->
- <property file="build.properties"/>
-
- <!-- The default.properties file is created and updated by the 'android' tool, as well
- as ADT.
- This file is an integral part of the build system for your application and
- should be checked in in Version Control Systems. -->
- <property file="default.properties"/>
-
- <!-- Custom Android task to deal with the project target, and import the proper rules.
- This requires ant 1.6.0 or above. -->
- <path id="android.antlibs">
- <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
- <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
- <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
- <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
- <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
- </path>
-
- <taskdef name="setup"
- classname="com.android.ant.SetupTask"
- classpathref="android.antlibs"/>
-
- <!-- Execute the Android Setup task that will setup some properties specific to the target,
- and import the rules files.
- To customize the rules, copy/paste them below the task, and disable import by setting
- the import attribute to false:
- <setup import="false" />
-
- This will ensure that the properties are setup correctly but that your customized
- targets are used.
- -->
- <setup />
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
+
- <target name="jarsigner" description="Sign the jar">
- <signjar keystore="ruboto-key.keystore" storepass="ruboto" jar="bin/IRB-unsigned.apk" alias="ruboto" />
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
</target>
-
- <target name="clean" description="clean">
- <delete dir="bin"/>
- <delete dir="gen"/>
+ <target name="-pre-compile">
</target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
</project>
View
BIN libs/jruby-core-1.6.4.jar → libs/jruby-core-1.7.0.dev.jar
Binary file not shown.
View
BIN libs/jruby-stdlib-1.6.4.jar → libs/jruby-stdlib-1.7.0.dev.jar
Binary file not shown.
View
40 proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
View
10 default.properties → project.properties
@@ -1,15 +1,15 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
+#
# This file must be checked in Version Control Systems.
-#
+#
# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
+# "ant.properties", and override values to adapt the script to your
# project structure.
+key.alias=ruboto
+key.store.password=ruboto
# Project target.
target=android-13
key.store=ruboto-key.keystore
-key.alias=ruboto
-key.store.password=ruboto
key.alias.password=ruboto
View
25 res/layout/get_ruboto_core.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|center_vertical"
+>
+<!--
+-->
+ <ImageButton
+ android:id="@+id/image"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:src="@drawable/icon"
+ android:layout_weight="1"
+ android:scaleType="fitCenter"
+ android:onClick="getRubotoCore"
+ />
+ <TextView android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Welcome to Ruboto! This is the first Ruboto application installed on this device. To continue, you need to install the Ruboto Core platform package. Click on the image above to go to the market and d\
+ownload and install the Ruboto Core platform package. Then return back here to start the app." />
+</LinearLayout>
View
2 res/values/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Ruboto IRB</string>
- <string name="version_name">0.6</string>
+ <string name="version_name">0.6.1</string>
<string name="ruboto_script_version">10</string>
<string name="IRB_Tab">IRB</string>
View
194 src/org/ruboto/EntryPointActivity.java
@@ -0,0 +1,194 @@
+package org.ruboto;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.ruboto.Script;
+
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class EntryPointActivity extends org.ruboto.RubotoActivity {
+ private int splash = 0;
+ private ProgressDialog loadingDialog;
+ private boolean dialogCancelled = false;
+ private BroadcastReceiver receiver;
+ private boolean appStarted = false;
+
+ public void onCreate(Bundle bundle) {
+ Log.d("RUBOTO", "onCreate: ");
+
+ try {
+ splash = Class.forName(getPackageName() + ".R$layout").getField("splash").getInt(null);
+ } catch (Exception e) {
+ splash = -1;
+ }
+
+ if (Script.isInitialized()) {
+ appStarted = true;
+ }
+ super.onCreate(bundle);
+ }
+
+ public void onResume() {
+ Log.d("RUBOTO", "onResume: ");
+
+ if(appStarted) {
+ Log.d("RUBOTO", "onResume: App already started!");
+ super.onResume();
+ return;
+ }
+
+ Log.d("RUBOTO", "onResume: Checking JRuby");
+ if (Script.isInitialized()) {
+ Log.d("RUBOTO", "Already initialized");
+ fireRubotoActivity();
+ } else {
+ Log.d("RUBOTO", "Not initialized");
+ showProgress();
+ receiver = new BroadcastReceiver(){
+ public void onReceive(Context context, Intent intent) {
+ Log.i("RUBOTO", "received broadcast: " + intent);
+ Log.i("RUBOTO", "URI: " + intent.getData());
+ if (intent.getData().toString().equals("package:org.ruboto.core")) {
+ Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
+ if (receiver != null) {
+ unregisterReceiver(receiver);
+ receiver = null;
+ }
+ showProgress();
+ initJRuby(false);
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ registerReceiver(receiver, filter);
+ initJRuby(true);
+ super.onResume();
+ }
+ }
+
+ public void onPause() {
+ Log.d("RUBOTO", "onPause: ");
+
+ if (receiver != null) {
+ unregisterReceiver(receiver);
+ receiver = null;
+ }
+ super.onPause();
+ }
+
+ public void onDestroy() {
+ Log.d("RUBOTO", "onDestroy: ");
+
+ super.onDestroy();
+ if (dialogCancelled) {
+ System.runFinalizersOnExit(true);
+ System.exit(0);
+ }
+ }
+
+ private void initJRuby(final boolean firstTime) {
+ new Thread(new Runnable() {
+ public void run() {
+ final boolean jrubyOk = Script.setUpJRuby(EntryPointActivity.this);
+ if (jrubyOk) {
+ Log.d("RUBOTO", "onResume: JRuby OK");
+ prepareJRuby();
+ runOnUiThread(new Runnable() {
+ public void run() {
+ fireRubotoActivity();
+ }
+ });
+ } else {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ if (firstTime) {
+ Log.d("RUBOTO", "onResume: Checking JRuby - IN UI thread");
+ try {
+ setContentView(Class.forName(getPackageName() + ".R$layout").getField("get_ruboto_core").getInt(null));
+ } catch (Exception e) {
+ }
+ } else {
+ Toast.makeText(EntryPointActivity.this,"Failed to initialize Ruboto Core.",Toast.LENGTH_SHORT).show();
+ try {
+ TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
+ textView.setText("Woops! Ruboto Core was installed, but it failed to initialize properly! I am not sure how to proceed from here. If you can, please file an error report at http://ruboto.org/");
+ } catch (Exception e) {
+ }
+ }
+ hideProgress();
+ }
+ });
+ }
+ }
+ }).start();
+ }
+
+ private static final String RUBOTO_APK = "RubotoCore-release.apk";
+ private static final String RUBOTO_URL = "https://github.com/downloads/ruboto/ruboto/" + RUBOTO_APK;
+
+ // Called when the button is pressed.
+ public void getRubotoCore(View view) {
+ try {
+ startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.ruboto.core")));
+ } catch (android.content.ActivityNotFoundException anfe) {
+ try {
+ TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
+ Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(RUBOTO_URL));
+ startActivity(intent);
+ } catch (Exception e) {}
+ }
+ }
+
+ private void fireRubotoActivity() {
+ if(appStarted) return;
+ appStarted = true;
+ Log.i("RUBOTO", "Starting activity");
+ loadScript();
+ onStart();
+ super.onResume();
+ hideProgress();
+ }
+
+ private void showProgress() {
+ if (loadingDialog == null) {
+ Log.i("RUBOTO", "Showing progress");
+ if (splash > 0) {
+ requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
+ setContentView(splash);
+ } else {
+ loadingDialog = ProgressDialog.show(this, null, "Starting...", true, true);
+ loadingDialog.setCanceledOnTouchOutside(false);
+ loadingDialog.setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ dialogCancelled = true;
+ finish();
+ }
+ });
+ }
+ }
+ }
+
+ private void hideProgress() {
+ if (loadingDialog != null) {
+ Log.d("RUBOTO", "Hide progress");
+ loadingDialog.dismiss();
+ loadingDialog = null;
+ }
+ }
+
+}
View
5 src/org/ruboto/RubotoActivity.java
@@ -130,12 +130,13 @@ protected void prepareJRuby() {
protected void loadScript() {
try {
if (scriptName != null) {
- new Script(scriptName).execute();
+ Script.setScriptFilename(getClass().getClassLoader().getResource(scriptName).getPath());
+ Script.execute(new Script(scriptName).getContents());
} else {
// TODO: Why doesn't this work?
// Script.callMethod(this, "initialize_ruboto");
Script.execute("$activity.initialize_ruboto");
- // TODO: Why doesn't this work?
+ // TODO: Why doesn't this work?
// Script.callMethod(this, "on_create", args[0]);
Script.execute("$activity.on_create($bundle)");
}
View
40 src/org/ruboto/RubotoBroadcastReceiver.java
@@ -1,5 +1,7 @@
package org.ruboto;
+import java.io.IOException;
+
public class RubotoBroadcastReceiver extends android.content.BroadcastReceiver {
private String scriptName = null;
private boolean initialized = false;
@@ -20,29 +22,37 @@ public RubotoBroadcastReceiver() {
public RubotoBroadcastReceiver(String name) {
super();
- if (name != null)
+ if (name != null) {
setScriptName(name);
+
+ if (Script.isInitialized()) {
+ loadScript();
+ }
+ }
}
- public void onReceive(android.content.Context context, android.content.Intent intent) {
- if (Script.setUpJRuby(context)) {
- Script.defineGlobalVariable("$context", context);
- Script.defineGlobalVariable("$broadcast_receiver", this);
- Script.defineGlobalVariable("$intent", intent);
-
+ protected void loadScript() {
+ Script.put("$broadcast_receiver", this);
+ if (scriptName != null) {
try {
- if (scriptName != null && !initialized) {
- new Script(scriptName).execute();
- initialized = true;
- } else {
- Script.execute("$broadcast_receiver.on_receive($context, $intent)");
- }
- } catch(Exception e) {
- e.printStackTrace();
+ new Script(scriptName).execute();
+ } catch(IOException e) {
+ throw new RuntimeException("IOException loading broadcast receiver script", e);
}
}
}
+ public void onReceive(android.content.Context context, android.content.Intent intent) {
+ Script.put("$context", context);
+ Script.put("$broadcast_receiver", this);
+ Script.put("$intent", intent);
+
+ try {
+ Script.execute("$broadcast_receiver.on_receive($context, $intent)");
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
}
View
6 src/org/ruboto/RubotoService.java
@@ -6,6 +6,7 @@
public class RubotoService extends android.app.Service {
private String scriptName;
+ private String remoteVariable = "";
public Object[] args;
public static final int CB_BIND = 0;
@@ -22,6 +23,11 @@ public void setCallbackProc(int id, Object obj) {
callbackProcs[id] = obj;
}
+ public RubotoService setRemoteVariable(String var) {
+ remoteVariable = ((var == null) ? "" : (var + "."));
+ return this;
+ }
+
public void setScriptName(String name){
scriptName = name;
}
View
142 src/org/ruboto/Script.java
@@ -31,9 +31,8 @@
private static String scriptsDir = "scripts";
private static File scriptsDirFile = null;
- protected String name = null;
+ private String name = null;
private static Object ruby;
- private static PrintStream output = null;
private static boolean initialized = false;
private static String localContextScope = "SINGLETON";
@@ -80,26 +79,49 @@ public static synchronized boolean setUpJRuby(Context appContext, PrintStream ou
System.setProperty("jruby.bytecode.version", "1.5");
System.setProperty("jruby.interfaces.useProxy", "true");
System.setProperty("jruby.management.enabled", "false");
+ System.setProperty("jruby.objectspace.enabled", "false");
+ System.setProperty("jruby.thread.pooling", "true");
+ System.setProperty("jruby.native.enabled", "false");
+
+ // Uncomment these to debug Ruby source loading
+ // System.setProperty("jruby.debug.loadService", "true");
+ // System.setProperty("jruby.debug.loadService.timing", "true");
+
ClassLoader classLoader;
Class<?> scriptingContainerClass;
+ String apkName = null;
try {
scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer");
System.out.println("Found JRuby in this APK");
classLoader = Script.class.getClassLoader();
+ try {
+ apkName = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0).sourceDir;
+ } catch (NameNotFoundException e) {}
} catch (ClassNotFoundException e1) {
- String packagePath = "org.ruboto.core";
- String apkName = null;
+ String packageName = "org.ruboto.core";
try {
- apkName = appContext.getPackageManager().getApplicationInfo(packagePath, 0).sourceDir;
+ apkName = appContext.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
} catch (PackageManager.NameNotFoundException e) {
System.out.println("JRuby not found");
return false;
}
System.out.println("Found JRuby in platform APK");
- classLoader = new PathClassLoader(apkName, Script.class.getClassLoader());
+ if (true) {
+ classLoader = new PathClassLoader(apkName, Script.class.getClassLoader());
+ } else {
+ // Alternative way to get the class loader. The other way is rumoured to have memory leaks.
+ try {
+ Context platformAppContext = appContext.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE + Context.CONTEXT_IGNORE_SECURITY);
+ classLoader = platformAppContext.getClassLoader();
+ } catch (PackageManager.NameNotFoundException e) {
+ System.out.println("Could not create package context even if application info could be found. Should never happen.");
+ return false;
+ }
+ }
+
try {
scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
} catch (ClassNotFoundException e) {
@@ -128,26 +150,38 @@ public static synchronized boolean setUpJRuby(Context appContext, PrintStream ou
Class compileModeClass = Class.forName("org.jruby.RubyInstanceConfig$CompileMode", true, classLoader);
callScriptingContainerMethod(Void.class, "setCompileMode", Enum.valueOf(compileModeClass, "OFF"));
+ // Class traceTypeClass = Class.forName("org.jruby.runtime.backtrace.TraceType", true, classLoader);
+ // Method traceTypeForMethod = traceTypeClass.getMethod("traceTypeFor", String.class);
+ // Object traceTypeRaw = traceTypeForMethod.invoke(null, "raw");
+ // callScriptingContainerMethod(Void.class, "setTraceType", traceTypeRaw);
+
+ // FIXME(uwe): Write tutorial on profiling.
+ // container.getProvider().getRubyInstanceConfig().setProfilingMode(mode);
+
// callScriptingContainerMethod(Void.class, "setClassLoader", classLoader);
Method setClassLoaderMethod = ruby.getClass().getMethod("setClassLoader", ClassLoader.class);
setClassLoaderMethod.invoke(ruby, classLoader);
Thread.currentThread().setContextClassLoader(classLoader);
- if (scriptsDir != null) {
- Log.d(TAG, "Setting JRuby current directory to " + scriptsDir);
- callScriptingContainerMethod(Void.class, "setCurrentDirectory", scriptsDir);
- }
+ String defaultCurrentDir = appContext.getFilesDir().getPath();
+ Log.d(TAG, "Setting JRuby current directory to " + defaultCurrentDir);
+ callScriptingContainerMethod(Void.class, "setCurrentDirectory", defaultCurrentDir);
+
if (out != null) {
// callScriptingContainerMethod(Void.class, "setOutput", out);
Method setOutputMethod = ruby.getClass().getMethod("setOutput", PrintStream.class);
setOutputMethod.invoke(ruby, out);
- output = out;
// callScriptingContainerMethod(Void.class, "setError", out);
Method setErrorMethod = ruby.getClass().getMethod("setError", PrintStream.class);
setErrorMethod.invoke(ruby, out);
}
+
+ String jrubyHome = "file:" + apkName + "!";
+ Log.i(TAG, "Setting JRUBY_HOME: " + jrubyHome);
+ System.setProperty("jruby.home", jrubyHome);
+
String extraScriptsDir = scriptsDirName(appContext);
Log.i(TAG, "Checking scripts in " + extraScriptsDir);
if (configDir(extraScriptsDir)) {
@@ -175,7 +209,7 @@ public static synchronized boolean setUpJRuby(Context appContext, PrintStream ou
private static void handleInitException(Exception e) {
Log.e(TAG, "Exception starting JRuby");
- Log.e(TAG, e.getMessage());
+ Log.e(TAG, e.getMessage() != null ? e.getMessage() : e.getClass().getName());
e.printStackTrace();
ruby = null;
}
@@ -226,11 +260,7 @@ public static Object exec(String code) {
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
} catch (java.lang.reflect.InvocationTargetException ite) {
-// throw ((RuntimeException) ite.getCause());
- for (java.lang.StackTraceElement ste : ite.getStackTrace()) {
- output.append(ste.toString() + "\n");
- }
- return null;
+ throw ((RuntimeException) ite.getCause());
}
}
@@ -278,38 +308,34 @@ public static File getDirFile() {
return scriptsDirFile;
}
- public static Boolean configDir(String noSdcard) {
- setDir(noSdcard);
- Method getLoadPathsMethod;
- List<String> loadPath = callScriptingContainerMethod(List.class, "getLoadPaths");
-
- if (!loadPath.contains(noSdcard)) {
- Log.i(TAG, "Adding scripts dir to load path: " + noSdcard);
- List<String> paths = loadPath;
- paths.add(noSdcard);
- // callScriptingContainerMethod(Void.class, "setLoadPaths", paths);
- try {
- Method setLoadPathsMethod = ruby.getClass().getMethod("setLoadPaths", List.class);
- setLoadPathsMethod.invoke(ruby, paths);
- } catch (NoSuchMethodException nsme) {
- throw new RuntimeException(nsme);
- } catch (IllegalAccessException iae) {
- throw new RuntimeException(iae);
- } catch (java.lang.reflect.InvocationTargetException ite) {
- throw new RuntimeException(ite);
- }
+ private static void setLoadPath(List<String> loadPath) {
+ // callScriptingContainerMethod(Void.class, "setLoadPaths", loadPath);
+ try {
+ Method setLoadPathsMethod = ruby.getClass().getMethod("setLoadPaths", List.class);
+ setLoadPathsMethod.invoke(ruby, loadPath);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(nsme);
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ } catch (java.lang.reflect.InvocationTargetException ite) {
+ throw new RuntimeException(ite);
}
+ }
- /* Create directory if it doesn't exist */
- if (!scriptsDirFile.exists()) {
- boolean dirCreatedOk = scriptsDirFile.mkdirs();
- if (!dirCreatedOk) {
- throw new RuntimeException("Unable to create script directory");
- }
+ private static List<String> getLoadPath() {
+ return callScriptingContainerMethod(List.class, "getLoadPaths");
+ }
+
+ public static Boolean configDir(String scriptsDir) {
+ if (new File(scriptsDir).exists()) {
+ Log.i(TAG, "Found extra scripts dir: " + scriptsDir);
+ setDir(scriptsDir);
+ exec("$:.unshift '" + scriptsDir + "' ; $:.uniq! ; p $:");
return true;
+ } else {
+ Log.i(TAG, "Extra scripts dir not present: " + scriptsDir);
+ return false;
}
-
- return false;
}
private static void copyScripts(String from, File to, AssetManager assets) {
@@ -431,7 +457,13 @@ public Script setName(String name) {
}
public String getContents() throws IOException {
- BufferedReader buffer = new BufferedReader(new java.io.InputStreamReader(getClass().getClassLoader().getResourceAsStream(name)), 8192);
+ InputStream is;
+ if (new File(scriptsDir + "/" + name).exists()) {
+ is = new java.io.FileInputStream(scriptsDir + "/" + name);
+ } else {
+ is = getClass().getClassLoader().getResourceAsStream(name);
+ }
+ BufferedReader buffer = new BufferedReader(new java.io.InputStreamReader(is), 8192);
StringBuilder source = new StringBuilder();
while (true) {
String line = buffer.readLine();
@@ -472,10 +504,7 @@ public static void callMethod(Object receiver, String methodName, Object[] args)
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
} catch (java.lang.reflect.InvocationTargetException ite) {
-// throw (RuntimeException) ite.getCause();
- for (java.lang.StackTraceElement ste : ite.getStackTrace()) {
- output.append(ste.toString() + "\n");
- }
+ throw (RuntimeException)(ite.getCause());
}
}
@@ -489,6 +518,7 @@ public static void callMethod(Object object, String methodName) {
@SuppressWarnings("unchecked")
public static <T> T callMethod(Object receiver, String methodName, Object[] args, Class<T> returnType) {
+ // return callScriptingContainerMethod(returnType, "callMethod", receiver, methodName, args, returnType);
try {
Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object[].class, Class.class);
return (T) callMethodMethod.invoke(ruby, receiver, methodName, args, returnType);
@@ -497,19 +527,17 @@ public static void callMethod(Object object, String methodName) {
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
} catch (java.lang.reflect.InvocationTargetException ite) {
-// throw (RuntimeException) ite.getCause();
- for (java.lang.StackTraceElement ste : ite.getStackTrace()) {
- output.append(ste.toString() + "\n");
- }
- return null;
+ throw (RuntimeException) ite.getCause();
}
}
- public static <T> T callMethod(Object receiver, String methodName, Object arg, Class<T> returnType) {
+ public static <T> T callMethod(Object receiver, String methodName,
+ Object arg, Class<T> returnType) {
return callMethod(receiver, methodName, new Object[]{arg}, returnType);
}
- public static <T> T callMethod(Object receiver, String methodName, Class<T> returnType) {
+ public static <T> T callMethod(Object receiver, String methodName,
+ Class<T> returnType) {
return callMethod(receiver, methodName, new Object[]{}, returnType);
}
View
2 src/org/ruboto/irb/IRBScript.java
@@ -117,7 +117,7 @@ public void save() throws IOException {
}
public String execute() throws IOException {
- IRBScript.setScriptFilename(this.name);
+ IRBScript.setScriptFilename(getName());
return Script.execute(getContents());
}
View
182 src/org/ruboto/irb/ScriptLaunch.java
@@ -1,190 +1,10 @@
package org.ruboto.irb;
-import java.io.IOException;
-
-import org.ruboto.Script;
-
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class ScriptLaunch extends org.ruboto.RubotoActivity {
- private int splash = 0;
- private ProgressDialog loadingDialog;
- private boolean dialogCancelled = false;
- private BroadcastReceiver receiver;
- private boolean appStarted = false;
+public class ScriptLaunch extends org.ruboto.EntryPointActivity {
public void onCreate(Bundle bundle) {
- Log.d("RUBOTO", "onCreate: ");
-
- try {
- splash = Class.forName(getPackageName() + ".R$layout").getField("splash").getInt(null);
- } catch (Exception e) {
- splash = -1;
- }
-
setScriptName("script_launch.rb");
- if (Script.isInitialized()) {
- appStarted = true;
- }
super.onCreate(bundle);
}
-
- public void onResume() {
- Log.d("RUBOTO", "onResume: ");
-
- if(appStarted) {
- Log.d("RUBOTO", "onResume: App already started!");
- super.onResume();
- return;
- }
-
- Log.d("RUBOTO", "onResume: Checking JRuby");
- if (Script.isInitialized()) {
- Log.d("RUBOTO", "Already initialized");
- fireRubotoActivity();
- } else {
- Log.d("RUBOTO", "Not initialized");
- showProgress();
- receiver = new BroadcastReceiver(){
- public void onReceive(Context context, Intent intent) {
- Log.i("RUBOTO", "received broadcast: " + intent);
- Log.i("RUBOTO", "URI: " + intent.getData());
- if (intent.getData().toString().equals("package:org.ruboto.core")) {
- Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
- if (receiver != null) {
- unregisterReceiver(receiver);
- receiver = null;
- }
- showProgress();
- initJRuby(false);
- }
- }
- };
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addDataScheme("package");
- registerReceiver(receiver, filter);
- initJRuby(true);
- super.onResume();
- }
- }
-
- public void onPause() {
- Log.d("RUBOTO", "onPause: ");
-
- if (receiver != null) {
- unregisterReceiver(receiver);
- receiver = null;
- }
- super.onPause();
- }
-
- public void onDestroy() {
- Log.d("RUBOTO", "onDestroy: ");
-
- super.onDestroy();
- if (dialogCancelled) {
- System.runFinalizersOnExit(true);
- System.exit(0);
- }
- }
-
- private void initJRuby(final boolean firstTime) {
- new Thread(new Runnable() {
- public void run() {
- final boolean jrubyOk = Script.setUpJRuby(ScriptLaunch.this);
- if (jrubyOk) {
- Log.d("RUBOTO", "onResume: JRuby OK");
- prepareJRuby();
- runOnUiThread(new Runnable() {
- public void run() {
- fireRubotoActivity();
- }
- });
- } else {
- runOnUiThread(new Runnable() {
- public void run() {
- if (firstTime) {
- Log.d("RUBOTO", "onResume: Checking JRuby - IN UI thread");
- try {
- setContentView(Class.forName(getPackageName() + ".R$layout").getField("get_ruboto_core").getInt(null));
- } catch (Exception e) {
- }
- } else {
- Toast.makeText(ScriptLaunch.this,"Failed to initialize Ruboto Core.",Toast.LENGTH_SHORT).show();
- try {
- TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
- textView.setText("Woops! Ruboto Core was installed, but it failed to initialize properly! I am not sure how to proceed from here. If you can, please file an error report at http://ruboto.org/");
- } catch (Exception e) {
- }
- }
- hideProgress();
- }
- });
- }
- }
- }).start();
- }
-
- // Called when buton is pressed.
- public void getRubotoCore(View view) {
- try {
- startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.ruboto.core")));
- } catch (android.content.ActivityNotFoundException anfe) {
- try {
- TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
- textView.setText("Could not find the Android Market App. You will have to install Ruboto Core manually. Bummer!");
- } catch (Exception e) {}
- }
- }
-
- private void fireRubotoActivity() {
- if(appStarted) return;
- appStarted = true;
- Log.i("RUBOTO", "Starting activity");
- loadScript();
- onStart();
- super.onResume();
- hideProgress();
- }
-
- private void showProgress() {
- if (loadingDialog == null) {
- Log.i("RUBOTO", "Showing progress");
- if (splash > 0) {
- requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
- setContentView(splash);
- } else {
- loadingDialog = ProgressDialog.show(this, null, "Starting...", true, true);
- loadingDialog.setCanceledOnTouchOutside(false);
- loadingDialog.setOnCancelListener(new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- dialogCancelled = true;
- finish();
- }
- });
- }
- }
- }
-
- private void hideProgress() {
- if (loadingDialog != null) {
- Log.d("RUBOTO", "Hide progress");
- loadingDialog.dismiss();
- loadingDialog = null;
- }
- }
-
}
View
24 src/ruboto.rb
@@ -0,0 +1,24 @@
+#######################################################
+#
+# ruboto.rb
+#
+# - Wrapper for using RubotoActivity, RubotoService, and
+# RubotoBroadcastReceiver.
+# - Provides interface for generating UI elements.
+# - Imports and configures callback classes.
+#
+# require this script for legacy support or require
+# the individual script files.
+#
+#######################################################
+
+require 'ruboto/activity'
+require 'ruboto/service'
+require 'ruboto/broadcast_receiver'
+
+require 'ruboto/widget'
+require 'ruboto/menu'
+require 'ruboto/util/stack'
+require 'ruboto/util/toast'
+require 'ruboto/legacy'
+
View
84 src/ruboto/activity.rb
@@ -0,0 +1,84 @@
+require 'ruboto/base'
+
+#######################################################
+#
+# ruboto/activity.rb
+#
+# Basic activity set up and callback configuration.
+#
+#######################################################
+
+#
+# Context
+#
+
+module Ruboto
+ module Context
+ def initialize_ruboto()
+ eval("#{$new_context_global} = self")
+ $new_context_global = nil
+
+ instance_eval &$context_init_block if $context_init_block
+ $context_init_block = nil
+ setup_ruboto_callbacks
+
+ @initialized = true
+ self
+ end
+
+ def start_ruboto_dialog(remote_variable, theme=Java::android.R.style::Theme_Dialog, &block)
+ ruboto_import "org.ruboto.RubotoDialog"
+ start_ruboto_activity(remote_variable, RubotoDialog, theme, &block)
+ end
+
+ def start_ruboto_activity(global_variable_name, klass=RubotoActivity, theme=nil, &block)
+ $context_init_block = block
+ $new_context_global = global_variable_name
+
+ if @initialized or (self == $activity && !$activity.kind_of?(RubotoActivity))
+ b = Java::android.os.Bundle.new
+ b.putInt("Theme", theme) if theme
+
+ i = Java::android.content.Intent.new
+ i.setClass self, klass.java_class
+ i.putExtra("RubotoActivity Config", b)
+
+ self.startActivity i
+ else
+ initialize_ruboto
+ on_create nil
+ end
+
+ self
+ end
+ end
+end
+
+java_import "android.content.Context"
+Context.class_eval do
+ include Ruboto::Context
+end
+
+#
+# Basic Activity Setup
+#
+
+module Ruboto
+ module Activity
+ end
+end
+
+def ruboto_configure_activity(klass)
+ klass.class_eval do
+ include Ruboto::Activity
+
+ # Can't be moved into the module
+ def on_create(bundle)
+ end
+ end
+end
+
+java_import "android.app.Activity"
+ruboto_import "org.ruboto.RubotoActivity"
+ruboto_configure_activity(RubotoActivity)
+
View
88 src/ruboto/base.rb
@@ -0,0 +1,88 @@
+#######################################################
+#
+# ruboto/base.rb
+#
+# Code shared by other ruboto components.
+#
+#######################################################
+
+# Only used needed for ruboto-core apps
+require 'ruboto/version'
+$RUBOTO_VERSION = 10
+
+def confirm_ruboto_version(required_version, exact=true)
+ raise "requires $RUBOTO_VERSION=#{required_version} or greater, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION < required_version and not exact
+ raise "requires $RUBOTO_VERSION=#{required_version}, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION != required_version and exact
+end
+
+require 'java'
+
+$package_name = ($activity || $service || $broadcast_receiver).package_name
+$package = eval("Java::#{$package_name}")
+
+# Create convenience method for top-level android package so we do not need to prefix with 'Java::'.
+module Kernel
+ def android
+ JavaUtilities.get_package_module_dot_format('android')
+ end
+end
+
+java_import "android.R"
+
+module Ruboto
+ java_import "#{$package_name}.R"
+ begin
+ Id = JavaUtilities.get_proxy_class("#{$package_name}.R$id")
+ rescue NameError
+ Java::android.util.Log.d "RUBOTO", "no R$id"
+ end
+end
+AndroidIds = JavaUtilities.get_proxy_class("android.R$id")
+
+#
+# Callbacks
+#
+
+module Ruboto
+ module CallbackClass
+ def new_with_callbacks &block
+ new.initialize_ruboto_callbacks &block
+ end
+ end
+
+ module Callbacks
+ def initialize_ruboto_callbacks &block
+ instance_eval &block
+ setup_ruboto_callbacks
+ self
+ end
+
+ def ruboto_callback_methods
+ (singleton_methods - ["on_create", "on_receive"]).select{|i| i =~ /^on_/}
+ end
+
+ def setup_ruboto_callbacks
+ ruboto_callback_methods.each do |i|
+ begin
+ setCallbackProc(self.class.const_get(i.sub(/^on_/, "CB_").upcase), method(i))
+ rescue
+ end
+ end
+ end
+ end
+end
+
+#
+# Import a class and set it up for handlers
+#
+
+def ruboto_import(package_class)
+ klass = java_import(package_class) || eval("Java::#{package_class}")
+ return unless klass
+
+ klass.class_eval do
+ extend Ruboto::CallbackClass
+ include Ruboto::Callbacks
+ end
+end
+
View
31 src/ruboto/broadcast_receiver.rb
@@ -0,0 +1,31 @@
+#######################################################
+#
+# ruboto/broadcast_receiver.rb
+#
+# Basic broadcast_receiver set up and callback configuration.
+#
+#######################################################
+
+require 'ruboto/base'
+
+ruboto_import "org.ruboto.RubotoBroadcastReceiver"
+RubotoBroadcastReceiver.class_eval do
+ def self.new_with_callbacks &block
+ (($broadcast_receiver.nil? || $broadcast_receiver.initialized) ? new : $broadcast_receiver).initialize_ruboto_callbacks &block
+ end
+
+ def initialized
+ @initialized ||= false
+ end
+
+ def initialize_ruboto_callbacks &block
+ instance_eval &block
+ setup_ruboto_callbacks
+ @initialized = true
+ self
+ end
+
+ def on_receive(context, intent)
+ end
+end
+
View
223 src/ruboto/legacy.rb
@@ -0,0 +1,223 @@
+require 'ruboto/activity'
+
+#######################################################
+#
+# ruboto/legacy.rb
+#
+# Required for backwards compatibility. The goal
+# should be to run scripts without this.
+#
+#######################################################
+
+#
+# Old handle methods
+#
+
+module Ruboto
+ module Callbacks
+ def method_missing(name, *args, &block)
+ if name.to_s =~ /^handle_(.*)/ and (const = self.class.const_get("CB_#{$1.upcase}"))
+ setCallbackProc(const, block)
+ self
+ else
+ super
+ end
+ end
+
+ def respond_to?(name)
+ return true if name.to_s =~ /^handle_(.*)/ and self.class.const_get("CB_#{$1.upcase}")
+ super
+ end
+
+ def initialize_handlers(&block)
+ instance_eval &block
+ self
+ end
+ end
+end
+
+#
+# Old handle_create for Activities
+#
+
+module Ruboto
+ module Activity
+ def handle_finish_create &block
+ @finish_create_block = block
+ end
+
+ def setup_content &block
+ @view_parent = nil
+ @content_view_block = block
+ end
+
+ def handle_create(&block)
+ $new_context_global = "$activity"
+ instance_exec &block
+ initialize_ruboto
+ on_create nil
+ end
+ end
+end
+
+RubotoActivity.class_eval do
+ def on_create(bundle)
+ @view_parent = nil
+ setContentView(instance_eval &@content_view_block) if @content_view_block
+ instance_eval { @finish_create_block.call } if @finish_create_block
+ end
+end
+
+#
+# Allow IRB legacy
+#
+
+if $package_name == "org.ruboto.irb"
+ Java::org.ruboto.irb.IRB.class_eval do
+ def handle_create(&block)
+ start_ruboto_activity "$activity", RubotoActivity, nil, &block
+ end
+ end
+end
+
+#
+# Legacy Service Subclass Setup
+#
+
+module Ruboto
+ module Service
+ def handle_create(&block)
+ $new_context_global = "$service"
+ instance_exec &block
+ initialize_ruboto
+ on_create
+ end
+ end
+end
+
+#
+# Legacy BroadcastReceiver Subclass Setup
+#
+
+module Ruboto
+ module BroadcastReceiver
+ def handle_receive(&block)
+ instance_exec &block
+ on_receive($context, nil)
+ end
+ end
+end
+
+#
+# Allows RubotoActivity to handle callbacks covering Class based handlers
+#
+
+def ruboto_register_handler(handler_class, unique_name, for_class, method_name)
+ klass_name = handler_class[/.+\.([A-Z].+)/, 1]
+ klass = ruboto_import handler_class
+
+ unless RubotoActivity.method_defined? "#{unique_name}_handler"
+ RubotoActivity.class_eval "
+ def #{unique_name}_handler
+ @#{unique_name}_handler ||= #{klass_name}.new
+ end
+