ruboto 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +19 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -0
- data/README.md +293 -0
- data/Rakefile +114 -0
- data/assets/Rakefile +386 -0
- data/assets/res/drawable-hdpi/icon.png +0 -0
- data/assets/res/drawable-ldpi/icon.png +0 -0
- data/assets/res/drawable-mdpi/icon.png +0 -0
- data/assets/res/layout/get_ruboto_core.xml +25 -0
- data/assets/samples/sample_activity.rb +21 -0
- data/assets/samples/sample_activity_test.rb +21 -0
- data/assets/samples/sample_broadcast_receiver.rb +6 -0
- data/assets/samples/sample_broadcast_receiver_test.rb +1 -0
- data/assets/samples/sample_service.rb +14 -0
- data/assets/samples/sample_service_test.rb +1 -0
- data/assets/src/InheritingActivity.java +195 -0
- data/assets/src/InheritingBroadcastReceiver.java +27 -0
- data/assets/src/InheritingClass.java +19 -0
- data/assets/src/InheritingService.java +9 -0
- data/assets/src/RubotoActivity.java +111 -0
- data/assets/src/RubotoBroadcastReceiver.java +51 -0
- data/assets/src/RubotoService.java +61 -0
- data/assets/src/org/ruboto/RubotoDialog.java +11 -0
- data/assets/src/org/ruboto/Script.java +545 -0
- data/assets/src/org/ruboto/test/ActivityTest.java +63 -0
- data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +124 -0
- data/assets/src/ruboto.rb +621 -0
- data/assets/test/assets/scripts/test_helper.rb +13 -0
- data/bin/ruboto +5 -0
- data/lib/java_class_gen/InheritingClass.java.erb +10 -0
- data/lib/java_class_gen/android_api.xml +1 -0
- data/lib/ruboto.rb +16 -0
- data/lib/ruboto/api.rb +21 -0
- data/lib/ruboto/commands/base.rb +392 -0
- data/lib/ruboto/core_ext/array.rb +6 -0
- data/lib/ruboto/core_ext/object.rb +10 -0
- data/lib/ruboto/util/asset_copier.rb +27 -0
- data/lib/ruboto/util/build.rb +201 -0
- data/lib/ruboto/util/code_formatting.rb +22 -0
- data/lib/ruboto/util/log_action.rb +20 -0
- data/lib/ruboto/util/main_fix.rb +13 -0
- data/lib/ruboto/util/objectspace.rb +8 -0
- data/lib/ruboto/util/scan_in_api.rb +40 -0
- data/lib/ruboto/util/update.rb +420 -0
- data/lib/ruboto/util/verify.rb +87 -0
- data/lib/ruboto/util/xml_element.rb +200 -0
- data/lib/ruboto/version.rb +3 -0
- data/test/activity/image_button_activity.rb +21 -0
- data/test/activity/image_button_activity_test.rb +21 -0
- data/test/activity/image_button_and_button_activity.rb +24 -0
- data/test/activity/image_button_and_button_activity_test.rb +27 -0
- data/test/activity/option_menu_activity.rb +21 -0
- data/test/activity/option_menu_activity_test.rb +20 -0
- data/test/activity/stack_activity.rb +21 -0
- data/test/activity/stack_activity_test.rb +23 -0
- data/test/app_test_methods.rb +41 -0
- data/test/minimal_app_test.rb +23 -0
- data/test/rake_test.rb +40 -0
- data/test/ruboto_gen_test.rb +32 -0
- data/test/ruboto_gen_with_psych_test.rb +16 -0
- data/test/ruboto_update_test.rb +5 -0
- data/test/ruboto_update_with_psych_test.rb +18 -0
- data/test/service_test.rb +49 -0
- data/test/test_helper.rb +177 -0
- data/test/update_test_methods.rb +33 -0
- metadata +157 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
package org.ruboto.test;
|
2
|
+
|
3
|
+
import android.app.Activity;
|
4
|
+
import android.app.ProgressDialog;
|
5
|
+
import android.test.ActivityInstrumentationTestCase2;
|
6
|
+
import android.util.Log;
|
7
|
+
import java.io.BufferedReader;
|
8
|
+
import java.io.FileReader;
|
9
|
+
import java.io.InputStream;
|
10
|
+
import java.io.InputStreamReader;
|
11
|
+
import java.io.IOException;
|
12
|
+
import junit.framework.AssertionFailedError;
|
13
|
+
import junit.framework.Test;
|
14
|
+
import junit.framework.TestResult;
|
15
|
+
import junit.framework.TestSuite;
|
16
|
+
import org.ruboto.Script;
|
17
|
+
|
18
|
+
public class ActivityTest extends ActivityInstrumentationTestCase2 {
|
19
|
+
private final Object setup;
|
20
|
+
private final Object block;
|
21
|
+
private final String filename;
|
22
|
+
|
23
|
+
public ActivityTest(Class activityClass, String filename, Object setup, String name, Object block) {
|
24
|
+
super(activityClass.getPackage().getName(), activityClass);
|
25
|
+
this.filename = filename;
|
26
|
+
this.setup = setup;
|
27
|
+
setName(filename + "#" + name);
|
28
|
+
this.block = block;
|
29
|
+
Log.i(getClass().getName(), "Instance: " + getName());
|
30
|
+
}
|
31
|
+
|
32
|
+
public void runTest() throws Exception {
|
33
|
+
Log.i(getClass().getName(), "runTest");
|
34
|
+
Log.i(getClass().getName(), "runTest: " + getName());
|
35
|
+
if (Script.setUpJRuby(getActivity())) {
|
36
|
+
Log.i(getClass().getName(), "ruby ok");
|
37
|
+
try {
|
38
|
+
final Activity activity = getActivity();
|
39
|
+
Log.i(getClass().getName(), "activity ok");
|
40
|
+
runTestOnUiThread(new Runnable() {
|
41
|
+
public void run() {
|
42
|
+
String oldFile = Script.getScriptFilename();
|
43
|
+
|
44
|
+
Log.i(getClass().getName(), "calling setup");
|
45
|
+
Script.setScriptFilename(filename);
|
46
|
+
Script.callMethod(setup, "call", activity);
|
47
|
+
Log.i(getClass().getName(), "setup ok");
|
48
|
+
|
49
|
+
Script.setScriptFilename(filename);
|
50
|
+
Script.callMethod(block, "call", activity);
|
51
|
+
Script.setScriptFilename(oldFile);
|
52
|
+
}
|
53
|
+
});
|
54
|
+
} catch (Throwable t) {
|
55
|
+
throw new AssertionFailedError(t.getMessage() != null ? t.getMessage() : t.getClass().getName());
|
56
|
+
}
|
57
|
+
Log.i(getClass().getName(), "runTest ok");
|
58
|
+
} else {
|
59
|
+
throw new AssertionFailedError("Ruboto Core platform is missing.");
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
}
|
@@ -0,0 +1,124 @@
|
|
1
|
+
package org.ruboto.test;
|
2
|
+
|
3
|
+
import android.test.ActivityInstrumentationTestCase2;
|
4
|
+
import android.util.Log;
|
5
|
+
import java.io.BufferedReader;
|
6
|
+
import java.io.File;
|
7
|
+
import java.io.FileInputStream;
|
8
|
+
import java.io.InputStream;
|
9
|
+
import java.io.InputStreamReader;
|
10
|
+
import java.io.IOException;
|
11
|
+
import java.io.UnsupportedEncodingException;
|
12
|
+
import java.net.JarURLConnection;
|
13
|
+
import java.net.URL;
|
14
|
+
import java.net.URLDecoder;
|
15
|
+
import java.util.ArrayList;
|
16
|
+
import java.util.Arrays;
|
17
|
+
import java.util.Collections;
|
18
|
+
import java.util.Enumeration;
|
19
|
+
import java.util.jar.JarFile;
|
20
|
+
import java.util.jar.JarEntry;
|
21
|
+
import java.util.List;
|
22
|
+
import junit.framework.Test;
|
23
|
+
import junit.framework.TestCase;
|
24
|
+
import junit.framework.TestSuite;
|
25
|
+
import org.ruboto.Script;
|
26
|
+
|
27
|
+
public class InstrumentationTestRunner extends android.test.InstrumentationTestRunner {
|
28
|
+
private Class activityClass;
|
29
|
+
private Object setup;
|
30
|
+
private TestSuite suite;
|
31
|
+
|
32
|
+
public TestSuite getAllTests() {
|
33
|
+
Log.i(getClass().getName(), "Finding test scripts");
|
34
|
+
suite = new TestSuite("Sweet");
|
35
|
+
|
36
|
+
try {
|
37
|
+
if (Script.setUpJRuby(getTargetContext())) {
|
38
|
+
Script.defineGlobalVariable("$runner", this);
|
39
|
+
Script.defineGlobalVariable("$test", this);
|
40
|
+
Script.defineGlobalVariable("$suite", suite);
|
41
|
+
|
42
|
+
// TODO(uwe): Why doesn't this work?
|
43
|
+
// Script.copyScriptsIfNeeded(getContext());
|
44
|
+
|
45
|
+
loadScript("test_helper.rb");
|
46
|
+
|
47
|
+
// TODO(uwe): Why doesn't this work?
|
48
|
+
// String[] scripts = new File(Script.scriptsDirName(getContext())).list();
|
49
|
+
|
50
|
+
String[] scripts = getContext().getResources().getAssets().list("scripts");
|
51
|
+
for (String f : scripts) {
|
52
|
+
if (f.equals("test_helper.rb")) continue;
|
53
|
+
Log.i(getClass().getName(), "Found script: " + f);
|
54
|
+
loadScript(f);
|
55
|
+
}
|
56
|
+
} else {
|
57
|
+
addError(suite, new RuntimeException("Ruboto Core platform is missing"));
|
58
|
+
}
|
59
|
+
} catch (IOException e) {
|
60
|
+
addError(suite, e);
|
61
|
+
} catch (RuntimeException e) {
|
62
|
+
addError(suite, e);
|
63
|
+
}
|
64
|
+
return suite;
|
65
|
+
}
|
66
|
+
|
67
|
+
public void activity(Class activityClass) {
|
68
|
+
this.activityClass = activityClass;
|
69
|
+
}
|
70
|
+
|
71
|
+
public void setup(Object block) {
|
72
|
+
this.setup = block;
|
73
|
+
}
|
74
|
+
|
75
|
+
public void test(String name, Object block) {
|
76
|
+
if (android.os.Build.VERSION.SDK_INT <= 8) {
|
77
|
+
name ="runTest";
|
78
|
+
}
|
79
|
+
Test test = new ActivityTest(activityClass, Script.getScriptFilename(), setup, name, block);
|
80
|
+
suite.addTest(test);
|
81
|
+
Log.d(getClass().getName(), "Made test instance: " + test);
|
82
|
+
}
|
83
|
+
|
84
|
+
private void addError(TestSuite suite, Throwable t) {
|
85
|
+
Throwable cause = t;
|
86
|
+
while(cause != null) {
|
87
|
+
Log.e(getClass().getName(), "Exception loading tests: " + cause);
|
88
|
+
t = cause;
|
89
|
+
cause = t.getCause();
|
90
|
+
}
|
91
|
+
final Throwable rootCause = t;
|
92
|
+
rootCause.printStackTrace();
|
93
|
+
suite.addTest(new TestCase(t.getMessage()) {
|
94
|
+
public void runTest() throws java.lang.Throwable {
|
95
|
+
throw rootCause;
|
96
|
+
}
|
97
|
+
});
|
98
|
+
}
|
99
|
+
|
100
|
+
private void loadScript(String f) throws IOException {
|
101
|
+
// TODO(uwe): Why doesn't this work?
|
102
|
+
// InputStream is = new FileInputStream(Script.scriptsDirName(getContext()) + "/" + f);
|
103
|
+
|
104
|
+
InputStream is = getContext().getResources().getAssets().open("scripts/" + f);
|
105
|
+
BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
|
106
|
+
StringBuilder source = new StringBuilder();
|
107
|
+
while (true) {
|
108
|
+
String line = buffer.readLine();
|
109
|
+
if (line == null) break;
|
110
|
+
source.append(line).append("\n");
|
111
|
+
}
|
112
|
+
buffer.close();
|
113
|
+
|
114
|
+
Log.d(getClass().getName(), "Loading test script: " + f);
|
115
|
+
String oldFilename = Script.getScriptFilename();
|
116
|
+
Script.setScriptFilename(f);
|
117
|
+
Script.put("$script_code", source.toString());
|
118
|
+
Script.setScriptFilename(f);
|
119
|
+
Script.execute("$test.instance_eval($script_code)");
|
120
|
+
Script.setScriptFilename(oldFilename);
|
121
|
+
Log.d(getClass().getName(), "Test script " + f + " loaded");
|
122
|
+
}
|
123
|
+
|
124
|
+
}
|
@@ -0,0 +1,621 @@
|
|
1
|
+
#######################################################
|
2
|
+
#
|
3
|
+
# ruboto.rb
|
4
|
+
#
|
5
|
+
# - Wrapper for using RubotoActivity, RubotoService, and
|
6
|
+
# RubotoBroadcastReceiver.
|
7
|
+
# - Provides interface for generating UI elements.
|
8
|
+
# - Imports and configures callback classes.
|
9
|
+
#
|
10
|
+
#######################################################
|
11
|
+
|
12
|
+
require 'ruboto/version'
|
13
|
+
$RUBOTO_VERSION = 9
|
14
|
+
|
15
|
+
def confirm_ruboto_version(required_version, exact=true)
|
16
|
+
raise "requires $RUBOTO_VERSION=#{required_version} or greater, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION < required_version and not exact
|
17
|
+
raise "requires $RUBOTO_VERSION=#{required_version}, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION != required_version and exact
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'java'
|
21
|
+
|
22
|
+
$package_name = ($activity || $service || $broadcast_receiver).package_name
|
23
|
+
$package = eval("Java::#{$package_name}")
|
24
|
+
|
25
|
+
# Create convenience method for top-level android package so we do not need to prefix with 'Java::'.
|
26
|
+
module Kernel
|
27
|
+
def android
|
28
|
+
JavaUtilities.get_package_module_dot_format('android')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
java_import "android.R"
|
33
|
+
|
34
|
+
module Ruboto
|
35
|
+
java_import "#{$package_name}.R"
|
36
|
+
begin
|
37
|
+
Id = JavaUtilities.get_proxy_class("#{$package_name}.R$id")
|
38
|
+
rescue NameError
|
39
|
+
Java::android.util.Log.d "RUBOTO", "no R$id"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
AndroidIds = JavaUtilities.get_proxy_class("android.R$id")
|
43
|
+
|
44
|
+
class Object
|
45
|
+
def with_large_stack(opts = {}, &block)
|
46
|
+
opts = {:size => opts} if opts.is_a? Integer
|
47
|
+
opts = {:name => 'Block with large stack'}.update(opts)
|
48
|
+
exception = nil
|
49
|
+
result = nil
|
50
|
+
t = Thread.with_large_stack(opts, &proc{result = block.call rescue exception = $!})
|
51
|
+
t.join
|
52
|
+
raise exception if exception
|
53
|
+
result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Thread
|
58
|
+
def self.with_large_stack(opts = {}, &block)
|
59
|
+
opts = {:size => opts} if opts.is_a? Integer
|
60
|
+
stack_size_kb = opts.delete(:size) || 64
|
61
|
+
name = opts.delete(:name) || "Thread with large stack"
|
62
|
+
raise "Unknown option(s): #{opts.inspect}" unless opts.empty?
|
63
|
+
t = java.lang.Thread.new(nil, block, name, stack_size_kb * 1024)
|
64
|
+
t.start
|
65
|
+
t
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#############################################################################
|
70
|
+
#
|
71
|
+
# Activity
|
72
|
+
#
|
73
|
+
|
74
|
+
def setup_activity
|
75
|
+
java_import "android.app.Activity"
|
76
|
+
|
77
|
+
Activity.class_eval do
|
78
|
+
def start_ruboto_dialog(remote_variable, theme=R.style::Theme_Dialog, &block)
|
79
|
+
start_ruboto_activity(remote_variable, RubotoDialog, theme, &block)
|
80
|
+
end
|
81
|
+
|
82
|
+
def start_ruboto_activity(remote_variable, klass=RubotoActivity, theme=nil, &block)
|
83
|
+
$activity_init_block = block
|
84
|
+
|
85
|
+
if @initialized or (self == $activity && !$activity.kind_of?(RubotoActivity))
|
86
|
+
b = Java::android.os.Bundle.new
|
87
|
+
b.putInt("Theme", theme) if theme
|
88
|
+
b.putString("Remote Variable", remote_variable)
|
89
|
+
b.putBoolean("Define Remote Variable", true)
|
90
|
+
b.putString("Initialize Script", "#{remote_variable}.initialize_activity")
|
91
|
+
|
92
|
+
i = Java::android.content.Intent.new
|
93
|
+
i.setClass self, klass.java_class
|
94
|
+
i.putExtra("RubotoActivity Config", b)
|
95
|
+
|
96
|
+
self.startActivity i
|
97
|
+
else
|
98
|
+
instance_eval "#{remote_variable}=self"
|
99
|
+
setRemoteVariable remote_variable
|
100
|
+
initialize_activity
|
101
|
+
on_create nil
|
102
|
+
end
|
103
|
+
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
#plugin
|
108
|
+
def toast(text, duration=5000)
|
109
|
+
Java::android.widget.Toast.makeText(self, text, duration).show
|
110
|
+
end
|
111
|
+
|
112
|
+
#plugin
|
113
|
+
def toast_result(result, success, failure, duration=5000)
|
114
|
+
toast(result ? success : failure, duration)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#############################################################################
|
120
|
+
#
|
121
|
+
# Configure a class to work with handlers
|
122
|
+
#
|
123
|
+
|
124
|
+
def ruboto_allow_handlers(klass)
|
125
|
+
klass.class_eval do
|
126
|
+
def method_missing(name, *args, &block)
|
127
|
+
if name.to_s =~ /^handle_(.*)/ and (const = self.class.const_get("CB_#{$1.upcase}"))
|
128
|
+
setCallbackProc(const, block)
|
129
|
+
self
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def respond_to?(name)
|
136
|
+
return true if name.to_s =~ /^handle_(.*)/ and self.class.const_get("CB_#{$1.upcase}")
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def initialize_handlers(&block)
|
141
|
+
instance_eval &block
|
142
|
+
self
|
143
|
+
end
|
144
|
+
end
|
145
|
+
klass
|
146
|
+
end
|
147
|
+
|
148
|
+
#############################################################################
|
149
|
+
#
|
150
|
+
# Activity Subclass Setup
|
151
|
+
#
|
152
|
+
|
153
|
+
def ruboto_configure_activity(klass)
|
154
|
+
klass.class_eval do
|
155
|
+
#
|
156
|
+
# Initialize
|
157
|
+
#
|
158
|
+
|
159
|
+
def initialize_activity()
|
160
|
+
instance_eval &$activity_init_block
|
161
|
+
@initialized = true
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
def handle_finish_create &block
|
166
|
+
@finish_create_block = block
|
167
|
+
end
|
168
|
+
|
169
|
+
def setup_content &block
|
170
|
+
@content_view_block = block
|
171
|
+
end
|
172
|
+
|
173
|
+
def on_create(bundle)
|
174
|
+
@view_parent = nil
|
175
|
+
setContentView(instance_eval &@content_view_block) if @content_view_block
|
176
|
+
instance_eval { @finish_create_block.call } if @finish_create_block
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Option Menus
|
181
|
+
#
|
182
|
+
|
183
|
+
def add_menu title, icon=nil, &block
|
184
|
+
mi = @menu.add(title)
|
185
|
+
mi.setIcon(icon) if icon
|
186
|
+
mi.class.class_eval { attr_accessor :on_click }
|
187
|
+
mi.on_click = block
|
188
|
+
|
189
|
+
# Seems to be needed or the block might get cleaned up
|
190
|
+
@all_menu_items = [] unless @all_menu_items
|
191
|
+
@all_menu_items << mi
|
192
|
+
end
|
193
|
+
|
194
|
+
def handle_create_options_menu &block
|
195
|
+
p = Proc.new do |*args|
|
196
|
+
@menu = args[0]
|
197
|
+
instance_eval { block.call(*args) } if block
|
198
|
+
end
|
199
|
+
setCallbackProc(self.class.const_get("CB_CREATE_OPTIONS_MENU"), p)
|
200
|
+
|
201
|
+
p = Proc.new do |num, menu_item|
|
202
|
+
# handles a problem where this is called for context items
|
203
|
+
# TODO(uwe): JRUBY-5866 JRuby can't access nested Java class if the class is called 'id'
|
204
|
+
# TODO(uwe): Remove check for SDK version when we stop supporting api level < 11
|
205
|
+
unless @just_processed_context_item == menu_item || (android.os.Build::VERSION::SDK_INT >= 11 && menu_item.item_id == AndroidIds.home)
|
206
|
+
instance_eval &(menu_item.on_click)
|
207
|
+
@just_processed_context_item = nil
|
208
|
+
true
|
209
|
+
else
|
210
|
+
false
|
211
|
+
end
|
212
|
+
end
|
213
|
+
setCallbackProc(self.class.const_get("CB_MENU_ITEM_SELECTED"), p)
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Context Menus
|
218
|
+
#
|
219
|
+
|
220
|
+
def add_context_menu title, &block
|
221
|
+
mi = @context_menu.add(title)
|
222
|
+
mi.class.class_eval { attr_accessor :on_click }
|
223
|
+
mi.on_click = block
|
224
|
+
|
225
|
+
# Seems to be needed or the block might get cleaned up
|
226
|
+
@all_menu_items = [] unless @all_menu_items
|
227
|
+
@all_menu_items << mi
|
228
|
+
end
|
229
|
+
|
230
|
+
def handle_create_context_menu &block
|
231
|
+
p = Proc.new do |*args|
|
232
|
+
@context_menu = args[0]
|
233
|
+
instance_eval { block.call(*args) } if block
|
234
|
+
end
|
235
|
+
setCallbackProc(self.class.const_get("CB_CREATE_CONTEXT_MENU"), p)
|
236
|
+
|
237
|
+
p = Proc.new do |menu_item|
|
238
|
+
if menu_item.on_click
|
239
|
+
arg = menu_item
|
240
|
+
begin
|
241
|
+
arg = menu_item.getMenuInfo.position
|
242
|
+
rescue
|
243
|
+
end
|
244
|
+
instance_eval { menu_item.on_click.call(arg) }
|
245
|
+
@just_processed_context_item = menu_item
|
246
|
+
true
|
247
|
+
else
|
248
|
+
false
|
249
|
+
end
|
250
|
+
end
|
251
|
+
setCallbackProc(self.class.const_get("CB_CONTEXT_ITEM_SELECTED"), p)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
ruboto_allow_handlers(klass)
|
256
|
+
end
|
257
|
+
|
258
|
+
#############################################################################
|
259
|
+
#
|
260
|
+
# Ruboto Set up for all app types (Activity, Service, BroadcastReceiver)
|
261
|
+
#
|
262
|
+
|
263
|
+
def ruboto_setup(klass, init_method = true)
|
264
|
+
# Setup ability to handle callbacks
|
265
|
+
ruboto_allow_handlers(klass)
|
266
|
+
|
267
|
+
if init_method
|
268
|
+
klass.class_eval do
|
269
|
+
eval %Q{
|
270
|
+
def handle_create(&block)
|
271
|
+
instance_exec &block
|
272
|
+
#{klass == Java::org.ruboto.RubotoActivity ? "on_create(nil)" : ""}
|
273
|
+
end
|
274
|
+
}
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
#############################################################################
|
280
|
+
#
|
281
|
+
# RubotoActivity View Generation
|
282
|
+
#
|
283
|
+
|
284
|
+
def ruboto_import_widgets(*widgets)
|
285
|
+
widgets.each { |i| ruboto_import_widget i }
|
286
|
+
end
|
287
|
+
|
288
|
+
def ruboto_import_widget(class_name, package_name="android.widget")
|
289
|
+
view_class = java_import "#{package_name}.#{class_name}"
|
290
|
+
|
291
|
+
RubotoActivity.class_eval "
|
292
|
+
def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={})
|
293
|
+
force_style = params.delete(:default_style)
|
294
|
+
force_parent = params.delete(:parent)
|
295
|
+
force_index = params.delete(:parent_index)
|
296
|
+
if force_style
|
297
|
+
if params.any?
|
298
|
+
attributes = android.util.AttributeSet.impl do |method, *args|
|
299
|
+
puts 'Not implemented, yet.'
|
300
|
+
puts %Q{Unhandled AttributeSet method: \#{method}(\#{args.map{|a| a.inspect}.join(', ')})}
|
301
|
+
end
|
302
|
+
else
|
303
|
+
attributes = nil
|
304
|
+
end
|
305
|
+
rv = #{class_name}.new(self, attributes, force_style)
|
306
|
+
else
|
307
|
+
if api_key = params.delete(:apiKey)
|
308
|
+
rv = #{class_name}.new(self, api_key)
|
309
|
+
else
|
310
|
+
rv = #{class_name}.new(self)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
if force_index
|
315
|
+
(force_parent || @view_parent).addView(rv, force_index) if (force_parent || @view_parent)
|
316
|
+
else
|
317
|
+
(force_parent || @view_parent).addView(rv) if (force_parent || @view_parent)
|
318
|
+
end
|
319
|
+
|
320
|
+
rv.configure self, params
|
321
|
+
|
322
|
+
if block_given?
|
323
|
+
old_view_parent, @view_parent = @view_parent, rv
|
324
|
+
yield
|
325
|
+
@view_parent = old_view_parent
|
326
|
+
end
|
327
|
+
rv
|
328
|
+
end
|
329
|
+
"
|
330
|
+
|
331
|
+
setup_list_view if class_name == :ListView
|
332
|
+
setup_spinner if class_name == :Spinner
|
333
|
+
setup_button if class_name == :Button
|
334
|
+
setup_image_button if class_name == :ImageButton
|
335
|
+
setup_linear_layout if class_name == :LinearLayout
|
336
|
+
setup_relative_layout if class_name == :RelativeLayout
|
337
|
+
end
|
338
|
+
|
339
|
+
#############################################################################
|
340
|
+
#
|
341
|
+
# Extend Common View Classes
|
342
|
+
#
|
343
|
+
|
344
|
+
def setup_view
|
345
|
+
java_import "android.view.View"
|
346
|
+
java_import "android.view.ViewGroup"
|
347
|
+
|
348
|
+
View.class_eval do
|
349
|
+
@@convert_constants ||= {}
|
350
|
+
|
351
|
+
def self.add_constant_conversion(from, to)
|
352
|
+
@@convert_constants[from] = to
|
353
|
+
end
|
354
|
+
|
355
|
+
def self.convert_constant(from)
|
356
|
+
@@convert_constants[from] or from
|
357
|
+
end
|
358
|
+
|
359
|
+
def self.setup_constant_conversion
|
360
|
+
(self.constants - self.superclass.constants).each do |i|
|
361
|
+
View.add_constant_conversion i.downcase.to_sym, self.const_get(i)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def configure(context, params = {})
|
366
|
+
if width = params.delete(:width)
|
367
|
+
getLayoutParams.width = View.convert_constant(width)
|
368
|
+
end
|
369
|
+
|
370
|
+
if height = params.delete(:height)
|
371
|
+
getLayoutParams.height = View.convert_constant(height)
|
372
|
+
end
|
373
|
+
|
374
|
+
if layout = params.delete(:layout)
|
375
|
+
lp = getLayoutParams
|
376
|
+
layout.each do |k, v|
|
377
|
+
values = (v.is_a?(Array) ? v : [v]).map { |i| @@convert_constants[i] or i }
|
378
|
+
lp.send("#{k.to_s.gsub(/_([a-z])/) { $1.upcase }}", *values)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
params.each do |k, v|
|
383
|
+
values = (v.is_a?(Array) ? v : [v]).map { |i| @@convert_constants[i] or i }
|
384
|
+
self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", *values)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
View.add_constant_conversion :wrap_content, ViewGroup::LayoutParams::WRAP_CONTENT
|
390
|
+
View.add_constant_conversion :fill_parent, ViewGroup::LayoutParams::FILL_PARENT
|
391
|
+
end
|
392
|
+
|
393
|
+
#############################################################################
|
394
|
+
#
|
395
|
+
# Special widget setup
|
396
|
+
#
|
397
|
+
|
398
|
+
def setup_linear_layout
|
399
|
+
LinearLayout.setup_constant_conversion
|
400
|
+
end
|
401
|
+
|
402
|
+
def setup_relative_layout
|
403
|
+
RelativeLayout.setup_constant_conversion
|
404
|
+
end
|
405
|
+
|
406
|
+
def setup_button
|
407
|
+
Button.class_eval do
|
408
|
+
def configure(context, params = {})
|
409
|
+
setOnClickListener(context)
|
410
|
+
super(context, params)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
ruboto_register_handler("org.ruboto.callbacks.RubotoOnClickListener", "click", Button, "setOnClickListener")
|
415
|
+
end
|
416
|
+
|
417
|
+
def setup_image_button
|
418
|
+
ImageButton.class_eval do
|
419
|
+
def configure(context, params = {})
|
420
|
+
setOnClickListener(context)
|
421
|
+
super(context, params)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
ruboto_register_handler("org.ruboto.callbacks.RubotoOnClickListener", "click", ImageButton, "setOnClickListener")
|
426
|
+
end
|
427
|
+
|
428
|
+
def setup_list_view
|
429
|
+
ListView.class_eval do
|
430
|
+
attr_reader :adapter, :adapter_list
|
431
|
+
|
432
|
+
def configure(context, params = {})
|
433
|
+
if params.has_key? :list
|
434
|
+
@adapter_list = Java::java.util.ArrayList.new
|
435
|
+
@adapter_list.addAll(params[:list])
|
436
|
+
item_layout = params.delete(:item_layout) || R::layout::simple_list_item_1
|
437
|
+
@adapter = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list)
|
438
|
+
setAdapter @adapter
|
439
|
+
params.delete :list
|
440
|
+
end
|
441
|
+
if params.has_key? :adapter
|
442
|
+
@adapter = params[:adapter]
|
443
|
+
end
|
444
|
+
setOnItemClickListener(context)
|
445
|
+
super(context, params)
|
446
|
+
end
|
447
|
+
|
448
|
+
def reload_list(list)
|
449
|
+
@adapter_list.clear
|
450
|
+
@adapter_list.addAll(list)
|
451
|
+
@adapter.notifyDataSetChanged
|
452
|
+
invalidate
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
ruboto_register_handler("org.ruboto.callbacks.RubotoOnItemClickListener", "item_click", ListView, "setOnItemClickListener")
|
457
|
+
end
|
458
|
+
|
459
|
+
def setup_spinner
|
460
|
+
Spinner.class_eval do
|
461
|
+
attr_reader :adapter, :adapter_list
|
462
|
+
|
463
|
+
def configure(context, params = {})
|
464
|
+
if params.has_key? :list
|
465
|
+
@adapter_list = Java::java.util.ArrayList.new
|
466
|
+
@adapter_list.addAll(params[:list])
|
467
|
+
item_layout = params.delete(:item_layout) || R::layout::simple_spinner_item
|
468
|
+
@adapter = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list)
|
469
|
+
@adapter.setDropDownViewResource(params.delete(:dropdown_layout) || R::layout::simple_spinner_dropdown_item)
|
470
|
+
setAdapter @adapter
|
471
|
+
params.delete :list
|
472
|
+
end
|
473
|
+
setOnItemSelectedListener(context)
|
474
|
+
super(context, params)
|
475
|
+
end
|
476
|
+
|
477
|
+
def reload_list(list)
|
478
|
+
@adapter.clear
|
479
|
+
@adapter.addAll(list)
|
480
|
+
@adapter.notifyDataSetChanged
|
481
|
+
invalidate
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
ruboto_register_handler("org.ruboto.callbacks.RubotoOnItemSelectedListener", "item_selected", Spinner, "setOnItemSelectedListener")
|
486
|
+
end
|
487
|
+
|
488
|
+
#############################################################################
|
489
|
+
#
|
490
|
+
# Import a class and set it up for handlers
|
491
|
+
#
|
492
|
+
|
493
|
+
def ruboto_import(package_class)
|
494
|
+
klass = java_import package_class
|
495
|
+
return unless klass
|
496
|
+
ruboto_allow_handlers(klass)
|
497
|
+
end
|
498
|
+
|
499
|
+
#############################################################################
|
500
|
+
#
|
501
|
+
# Allows RubotoActivity to handle callbacks covering Class based handlers
|
502
|
+
#
|
503
|
+
def ruboto_register_handler(handler_class, unique_name, for_class, method_name)
|
504
|
+
klass_name = handler_class[/.+\.([A-Z].+)/, 1]
|
505
|
+
klass = ruboto_import handler_class
|
506
|
+
|
507
|
+
unless RubotoActivity.method_defined? "#{unique_name}_handler"
|
508
|
+
RubotoActivity.class_eval "
|
509
|
+
def #{unique_name}_handler
|
510
|
+
@#{unique_name}_handler ||= #{klass_name}.new
|
511
|
+
end
|
512
|
+
|
513
|
+
def handle_#{unique_name}(&block)
|
514
|
+
#{unique_name}_handler.handle_#{unique_name} &block
|
515
|
+
self
|
516
|
+
end
|
517
|
+
"
|
518
|
+
end
|
519
|
+
|
520
|
+
unless for_class.method_defined? "orig_#{method_name}"
|
521
|
+
for_class.class_eval "
|
522
|
+
alias_method :orig_#{method_name}, :#{method_name}
|
523
|
+
def #{method_name}(handler)
|
524
|
+
orig_#{method_name}(handler.kind_of?(RubotoActivity) ? handler.#{unique_name}_handler : handler)
|
525
|
+
end
|
526
|
+
"
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
#############################################################################
|
531
|
+
#
|
532
|
+
# RubotoPreferenceActivity Preference Generation
|
533
|
+
#
|
534
|
+
|
535
|
+
def ruboto_import_preferences(*preferences)
|
536
|
+
preferences.each { |i| ruboto_import_preference i }
|
537
|
+
end
|
538
|
+
|
539
|
+
def ruboto_import_preference(class_name, package_name="android.preference")
|
540
|
+
klass = java_import "#{package_name}.#{class_name}"
|
541
|
+
return unless klass
|
542
|
+
|
543
|
+
setup_preferences
|
544
|
+
|
545
|
+
RubotoPreferenceActivity.class_eval "
|
546
|
+
def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={})
|
547
|
+
rv = #{class_name}.new self
|
548
|
+
rv.configure self, params
|
549
|
+
@parent.addPreference(rv) if @parent
|
550
|
+
if block_given?
|
551
|
+
old_parent, @parent = @parent, rv
|
552
|
+
yield
|
553
|
+
@parent = old_parent
|
554
|
+
end
|
555
|
+
rv
|
556
|
+
end
|
557
|
+
"
|
558
|
+
end
|
559
|
+
|
560
|
+
def setup_preferences
|
561
|
+
return if @preferences_setup_complete
|
562
|
+
|
563
|
+
java_import "android.preference.PreferenceScreen"
|
564
|
+
java_import "android.preference.Preference"
|
565
|
+
java_import "org.ruboto.RubotoPreferenceActivity"
|
566
|
+
ruboto_configure_activity(RubotoPreferenceActivity)
|
567
|
+
|
568
|
+
|
569
|
+
RubotoPreferenceActivity.class_eval do
|
570
|
+
def preference_screen(params={})
|
571
|
+
rv = self.getPreferenceManager.createPreferenceScreen(self)
|
572
|
+
rv.configure self, params
|
573
|
+
@parent.addPreference(rv) if @parent
|
574
|
+
if block_given?
|
575
|
+
old_parent, @parent = @parent, rv
|
576
|
+
yield
|
577
|
+
@parent = old_parent
|
578
|
+
end
|
579
|
+
rv
|
580
|
+
end
|
581
|
+
|
582
|
+
def setup_preference_screen &block
|
583
|
+
@preference_screen_block = block
|
584
|
+
end
|
585
|
+
|
586
|
+
def on_create(bundle)
|
587
|
+
@parent = nil
|
588
|
+
setPreferenceScreen(instance_eval &@preference_screen_block) if @preference_screen_block
|
589
|
+
instance_eval { @finish_create_block.call } if @finish_create_block
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
Preference.class_eval do
|
594
|
+
def configure(context, params = {})
|
595
|
+
params.each do |k, v|
|
596
|
+
if v.is_a?(Array)
|
597
|
+
self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", *v)
|
598
|
+
else
|
599
|
+
self.send("set#{k.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }}", v)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
@preferences_setup_complete = true
|
606
|
+
end
|
607
|
+
|
608
|
+
# Setup activity support
|
609
|
+
java_import "org.ruboto.RubotoActivity"
|
610
|
+
setup_activity
|
611
|
+
ruboto_configure_activity(RubotoActivity)
|
612
|
+
ruboto_setup(RubotoActivity)
|
613
|
+
setup_view
|
614
|
+
|
615
|
+
# setup service support
|
616
|
+
java_import "org.ruboto.RubotoService"
|
617
|
+
ruboto_setup(RubotoService)
|
618
|
+
|
619
|
+
# setup broadcast receiver support
|
620
|
+
java_import "org.ruboto.RubotoBroadcastReceiver"
|
621
|
+
ruboto_setup(RubotoBroadcastReceiver, false)
|