ruboto 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|