calatrava 0.0.1
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/.gitignore +9 -0
- data/.rvmrc +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +202 -0
- data/Plans.md +20 -0
- data/README.md +73 -0
- data/Rakefile +26 -0
- data/bin/calatrava +6 -0
- data/calatrava.gemspec +35 -0
- data/features/cli.feature +14 -0
- data/features/project.feature +48 -0
- data/features/step_definitions/template_steps.rb +8 -0
- data/features/support/env.rb +5 -0
- data/lib/calatrava/app.rb +30 -0
- data/lib/calatrava/manifest.rb +44 -0
- data/lib/calatrava/project.rb +218 -0
- data/lib/calatrava/resources_build_phase.rb +19 -0
- data/lib/calatrava/tasks/apache.rb +44 -0
- data/lib/calatrava/tasks/artifact.rb +24 -0
- data/lib/calatrava/tasks/assets.rb +6 -0
- data/lib/calatrava/tasks/automation.rb +38 -0
- data/lib/calatrava/tasks/bootstrap.rb +10 -0
- data/lib/calatrava/tasks/build.rb +1 -0
- data/lib/calatrava/tasks/configuration.rb +41 -0
- data/lib/calatrava/tasks/droid.rb +83 -0
- data/lib/calatrava/tasks/haml.rb +71 -0
- data/lib/calatrava/tasks/ios.rb +73 -0
- data/lib/calatrava/tasks/kernel.rb +52 -0
- data/lib/calatrava/tasks/precommit.rb +22 -0
- data/lib/calatrava/tasks/release.rb +17 -0
- data/lib/calatrava/tasks/shell.rb +17 -0
- data/lib/calatrava/tasks/web.rb +82 -0
- data/lib/calatrava/tasks.rb +93 -0
- data/lib/calatrava/template.rb +44 -0
- data/lib/calatrava/templates/.rvmrc.calatrava +2 -0
- data/lib/calatrava/templates/Gemfile.calatrava +4 -0
- data/lib/calatrava/templates/Rakefile +1 -0
- data/lib/calatrava/templates/assets/lib/ICanHaz.js +542 -0
- data/lib/calatrava/templates/assets/lib/underscore.js +1059 -0
- data/lib/calatrava/templates/assets/lib/zepto.js +1355 -0
- data/lib/calatrava/templates/build_env.sh +2 -0
- data/lib/calatrava/templates/config/environments.yml +17 -0
- data/lib/calatrava/templates/config/templates/env.coffee.erb +7 -0
- data/lib/calatrava/templates/config/templates/httpd.conf.erb +33 -0
- data/lib/calatrava/templates/droid/app/bridge.coffee +130 -0
- data/lib/calatrava/templates/droid/calatrava/ant/calatrava.xml +48 -0
- data/lib/calatrava/templates/droid/calatrava/calatrava-build.xml +91 -0
- data/lib/calatrava/templates/droid/calatrava/ivy.xml +8 -0
- data/lib/calatrava/templates/droid/calatrava/ivysettings.xml +12 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/AndroidManifest.xml.calatrava +20 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/ConversionForm.java.calatrava +27 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/Title.java.calatrava +23 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/CalatravaPage.java +13 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/AjaxRequestManager.java +166 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/AssetRepository.java +17 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/KernelBridge.java +25 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/Launcher.java +85 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/PageRegistry.java +225 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RegisteredActivity.java +86 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RequestLoader.java +31 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RhinoService.java +212 -0
- data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/shell/WebViewActivity.java +247 -0
- data/lib/calatrava/templates/droid/manifest.yml +1 -0
- data/lib/calatrava/templates/ios/Podfile.calatrava +5 -0
- data/lib/calatrava/templates/ios/manifest.yml +1 -0
- data/lib/calatrava/templates/ios/res/js/bridge.js +249 -0
- data/lib/calatrava/templates/ios/res/xibs/ProgressViewController.xib +334 -0
- data/lib/calatrava/templates/ios/res/xibs/WebViewController.xib +173 -0
- data/lib/calatrava/templates/ios/src/AppDelegate.h +8 -0
- data/lib/calatrava/templates/ios/src/AppDelegate.m +56 -0
- data/lib/calatrava/templates/ios/src/CALATRAVA_TMPL-Info.plist +45 -0
- data/lib/calatrava/templates/ios/src/CALATRAVA_TMPL-Prefix.pch +14 -0
- data/lib/calatrava/templates/ios/src/ConversionFormViewController.h +16 -0
- data/lib/calatrava/templates/ios/src/ConversionFormViewController.m +179 -0
- data/lib/calatrava/templates/ios/src/ConversionFormViewController.xib +357 -0
- data/lib/calatrava/templates/ios/src/en.lproj/InfoPlist.strings +2 -0
- data/lib/calatrava/templates/ios/src/main.m +10 -0
- data/lib/calatrava/templates/ios/test/AJAXConnectionTest.m +21 -0
- data/lib/calatrava/templates/ios/test/AppTests-Prefix.pch +8 -0
- data/lib/calatrava/templates/ios/test/CalatravaiOSTests-Info.plist +22 -0
- data/lib/calatrava/templates/ios/test/TWBridgePageRegistryTest.m +15 -0
- data/lib/calatrava/templates/ios/test/en.lproj/InfoPlist.strings +2 -0
- data/lib/calatrava/templates/kernel/.gitignore +1 -0
- data/lib/calatrava/templates/kernel/app/calatrava.coffee +8 -0
- data/lib/calatrava/templates/kernel/app/converter/controller.converter.coffee +50 -0
- data/lib/calatrava/templates/kernel/app/converter/init.converter.coffee +11 -0
- data/lib/calatrava/templates/kernel/app/pageHelper.coffee +17 -0
- data/lib/calatrava/templates/kernel/features/support/bridge.coffee +124 -0
- data/lib/calatrava/templates/kernel/features/support/spec_helper.js +49 -0
- data/lib/calatrava/templates/kernel/spec/converter/controller.converter.spec.coffee +37 -0
- data/lib/calatrava/templates/kernel/spec/environment.spec_helper.coffee +25 -0
- data/lib/calatrava/templates/kernel/spec/spec_helper.js +49 -0
- data/lib/calatrava/templates/kernel/spec/stubView.coffee +18 -0
- data/lib/calatrava/templates/kernel/watchr.rb +17 -0
- data/lib/calatrava/templates/package.json +20 -0
- data/lib/calatrava/templates/shell/layouts/single_page.haml +23 -0
- data/lib/calatrava/templates/shell/pages/converter/conversionForm.haml +12 -0
- data/lib/calatrava/templates/shell/pages/converter/page.conversionForm.coffee +42 -0
- data/lib/calatrava/templates/shell/shell.scss +1 -0
- data/lib/calatrava/templates/shell/support/shell.coffee +21 -0
- data/lib/calatrava/templates/web/apache/conf/mime.types +1357 -0
- data/lib/calatrava/templates/web/app/source/bridge.coffee +158 -0
- data/lib/calatrava/templates/web/app/source/init.coffee +14 -0
- data/lib/calatrava/templates/web/app/views/index.haml +18 -0
- data/lib/calatrava/templates/web/deploy/instance.sh +10 -0
- data/lib/calatrava/templates/web/manifest.yml +1 -0
- data/lib/calatrava/version.rb +3 -0
- data/lib/calatrava.rb +5 -0
- metadata +302 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.calatrava.bridge;
|
|
2
|
+
|
|
3
|
+
import android.app.Application;
|
|
4
|
+
import android.content.ComponentName;
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.content.ServiceConnection;
|
|
8
|
+
import android.os.IBinder;
|
|
9
|
+
import android.util.Log;
|
|
10
|
+
|
|
11
|
+
import java.io.IOException;
|
|
12
|
+
import java.io.BufferedReader;
|
|
13
|
+
import java.io.InputStreamReader;
|
|
14
|
+
|
|
15
|
+
public class Launcher {
|
|
16
|
+
private static String TAG = Launcher.class.getSimpleName();
|
|
17
|
+
|
|
18
|
+
private static String appName;
|
|
19
|
+
private static RhinoService rhino;
|
|
20
|
+
private static Context appContext;
|
|
21
|
+
private static Application application;
|
|
22
|
+
private static Runnable startUp;
|
|
23
|
+
|
|
24
|
+
static ServiceConnection connection = new ServiceConnection() {
|
|
25
|
+
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
|
|
26
|
+
{
|
|
27
|
+
try
|
|
28
|
+
{
|
|
29
|
+
rhino = ((RhinoService.LocalBinder) iBinder).getService();
|
|
30
|
+
PageRegistry.setSharedRegistry(new PageRegistry(appName, appContext, application, rhino));
|
|
31
|
+
AjaxRequestManager.setSharedManager(new AjaxRequestManager(appContext, rhino));
|
|
32
|
+
initBridge();
|
|
33
|
+
startUp.run();
|
|
34
|
+
}
|
|
35
|
+
catch (Exception e)
|
|
36
|
+
{
|
|
37
|
+
Log.e(TAG, "Unable to start.", e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public void onServiceDisconnected(ComponentName componentName) {
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
public static void launchKernel(String appName,
|
|
47
|
+
Context appContext,
|
|
48
|
+
Application application,
|
|
49
|
+
Runnable startUp) {
|
|
50
|
+
Launcher.appName = appName;
|
|
51
|
+
Launcher.appContext = appContext;
|
|
52
|
+
Launcher.application = application;
|
|
53
|
+
Launcher.startUp = startUp;
|
|
54
|
+
|
|
55
|
+
Intent serviceIntent = new Intent(appContext, RhinoService.class);
|
|
56
|
+
appContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public static void launchFlow(String flow)
|
|
60
|
+
{
|
|
61
|
+
rhino.callJsFunction(flow);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private static void initBridge() {
|
|
65
|
+
AssetRepository assets = new AssetRepository(appContext);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
Log.d(TAG, "About to prep the rhino");
|
|
69
|
+
rhino.initRhino();
|
|
70
|
+
|
|
71
|
+
Log.d(TAG, "About to load and start kernel");
|
|
72
|
+
// Load all the application JS
|
|
73
|
+
KernelBridge bridge = new KernelBridge(assets, rhino);
|
|
74
|
+
BufferedReader loadFileReader = new BufferedReader(new InputStreamReader(appContext.getAssets().open("hybrid/load_file.text")), 8192);
|
|
75
|
+
String line = null;
|
|
76
|
+
while ((line = loadFileReader.readLine()) != null)
|
|
77
|
+
{
|
|
78
|
+
bridge.loadLibrary(line);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} catch (IOException e) {
|
|
82
|
+
Log.d(TAG, "LauncherActivity failed to start: " + e);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
package com.calatrava.bridge;
|
|
2
|
+
|
|
3
|
+
import android.app.Application;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import android.content.ContextWrapper;
|
|
7
|
+
import android.net.Uri;
|
|
8
|
+
import android.util.Log;
|
|
9
|
+
import android.content.pm.PackageManager.NameNotFoundException;
|
|
10
|
+
|
|
11
|
+
import org.mozilla.javascript.ScriptableObject;
|
|
12
|
+
|
|
13
|
+
import com.calatrava.CalatravaPage;
|
|
14
|
+
|
|
15
|
+
import java.util.*;
|
|
16
|
+
|
|
17
|
+
import java.io.IOException;
|
|
18
|
+
import java.net.URISyntaxException;
|
|
19
|
+
|
|
20
|
+
import dalvik.system.DexFile;
|
|
21
|
+
import dalvik.system.PathClassLoader;
|
|
22
|
+
import dalvik.system.DexClassLoader;
|
|
23
|
+
|
|
24
|
+
import java.lang.annotation.Annotation;
|
|
25
|
+
|
|
26
|
+
public class PageRegistry {
|
|
27
|
+
public static String TAG = PageRegistry.class.getSimpleName();
|
|
28
|
+
private static PageRegistry sharedRegistry;
|
|
29
|
+
|
|
30
|
+
private Context appContext;
|
|
31
|
+
private RhinoService rhino;
|
|
32
|
+
private Map<String, Class<?>> pageFactories = new HashMap<String, Class<?>>();
|
|
33
|
+
private Map<String, RegisteredPage> registeredPages = new HashMap<String, RegisteredPage>();
|
|
34
|
+
|
|
35
|
+
public static PageRegistry sharedRegistry() {
|
|
36
|
+
return sharedRegistry;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static void setSharedRegistry(PageRegistry shared) {
|
|
40
|
+
Log.d(TAG, "Set shared page registry");
|
|
41
|
+
sharedRegistry = shared;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public PageRegistry(String appName, Context appContext, Application app, RhinoService rhino)
|
|
45
|
+
throws IOException, URISyntaxException, ClassNotFoundException, NameNotFoundException
|
|
46
|
+
{
|
|
47
|
+
this.appContext = appContext;
|
|
48
|
+
this.rhino = rhino;
|
|
49
|
+
|
|
50
|
+
// Find all the logical page classes in the app
|
|
51
|
+
Log.d(TAG, "Searching for Calatrava pages in '" + appName + "'");
|
|
52
|
+
addPages(appName, appContext);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private void addPages(String packageName, Context context)
|
|
56
|
+
throws IOException, URISyntaxException, ClassNotFoundException, NameNotFoundException
|
|
57
|
+
{
|
|
58
|
+
String apkName = context.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
|
|
59
|
+
DexFile dexFile = new DexFile(apkName);
|
|
60
|
+
PathClassLoader classLoader2 = new PathClassLoader(apkName, Thread.currentThread().getContextClassLoader());
|
|
61
|
+
DexClassLoader classLoader = new DexClassLoader(apkName, new ContextWrapper(context).getCacheDir().getAbsolutePath(), null, classLoader2);
|
|
62
|
+
|
|
63
|
+
Enumeration<String> entries = dexFile.entries();
|
|
64
|
+
while (entries.hasMoreElements())
|
|
65
|
+
{
|
|
66
|
+
String entry = entries.nextElement();
|
|
67
|
+
// only check items that exist in source package and not in libraries, etc.
|
|
68
|
+
if (entry.startsWith(packageName))
|
|
69
|
+
{
|
|
70
|
+
Class<?> entryClass = classLoader.loadClass(entry);
|
|
71
|
+
if (entryClass != null)
|
|
72
|
+
{
|
|
73
|
+
Annotation[] annotations = entryClass.getAnnotations();
|
|
74
|
+
for (Annotation annotation : annotations)
|
|
75
|
+
{
|
|
76
|
+
if (annotation instanceof CalatravaPage)
|
|
77
|
+
{
|
|
78
|
+
String pageName = ((CalatravaPage)annotation).name();
|
|
79
|
+
Log.d(TAG, "Registering Calatrava page: " + pageName);
|
|
80
|
+
pageFactories.put(pageName, entryClass);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public void changePage(String target) {
|
|
89
|
+
Log.d(TAG, "changePage('" + target + "')");
|
|
90
|
+
Class activityClass = pageFactories.get(target);
|
|
91
|
+
Log.d(TAG, "Activity to be started: " + activityClass.getSimpleName());
|
|
92
|
+
appContext.startActivity(new Intent(appContext, activityClass));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public void displayWidget(String name, String options) {
|
|
96
|
+
appContext.sendBroadcast(new Intent("com.calatrava.widget").putExtra("name", name).putExtra("options", options));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public void displayDialog(String dialogName) {
|
|
100
|
+
appContext.sendBroadcast(new Intent("com.calatrava.dialog").putExtra("name", dialogName));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public void alert(String message) {
|
|
104
|
+
Log.d(TAG, "Broadcasting alert message: '" + message + "'");
|
|
105
|
+
appContext.sendBroadcast(new Intent("com.calatrava.alert").putExtra("message", message));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public void openUrl(String url) {
|
|
109
|
+
Intent browser = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
110
|
+
appContext.startActivity(browser);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public void track(String pageName, String channel, String eventName, Object variables, Object properties) {
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public void startTimer(int timeout, final String timerId) {
|
|
117
|
+
final Timer timer = new Timer();
|
|
118
|
+
timer.schedule(new TimerTask() {
|
|
119
|
+
@Override
|
|
120
|
+
public void run() {
|
|
121
|
+
timer.cancel();
|
|
122
|
+
timer.purge();
|
|
123
|
+
rhino.callJsFunction("tw.bridge.timers.fireTimer", new String[] {timerId});
|
|
124
|
+
}
|
|
125
|
+
}, 1000 * timeout);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private HashMap<String, String> jsObjectToMap(ScriptableObject obj) {
|
|
129
|
+
HashMap<String, String> map = new HashMap<String, String>();
|
|
130
|
+
for (Object k : obj.getIds()) {
|
|
131
|
+
if (k instanceof String) {
|
|
132
|
+
map.put((String) k, ScriptableObject.getProperty(obj, (String) k).toString());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return map;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private void ensureRegisteredPage(String name, RegisteredActivity page) {
|
|
139
|
+
if (registeredPages.get(name) == null) {
|
|
140
|
+
registeredPages.put(name, new RegisteredPage(page));
|
|
141
|
+
} else if (page != null) {
|
|
142
|
+
registeredPages.get(name).setPage(page);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public void registerPage(String name, RegisteredActivity page) {
|
|
147
|
+
ensureRegisteredPage(name, page);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public void unregisterPage(String pageName) {
|
|
151
|
+
registeredPages.remove(pageName);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public String getValueForField(String page, String field) {
|
|
155
|
+
Log.d(TAG, "Requesting field '" + field + "' from page '" + page + "'");
|
|
156
|
+
ensureRegisteredPage(page, null);
|
|
157
|
+
return registeredPages.get(page).getFieldValue(field);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public void renderPage(String page, String renderJson) {
|
|
161
|
+
Log.d(TAG, "renderPage: '" + page + "' Response object: " + renderJson);
|
|
162
|
+
|
|
163
|
+
ensureRegisteredPage(page, null);
|
|
164
|
+
registeredPages.get(page).render(renderJson);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public void pageOffscreen(String page) {
|
|
168
|
+
ensureRegisteredPage(page, null);
|
|
169
|
+
registeredPages.get(page).pageOffscreen();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public void pageOnscreen(String page) {
|
|
173
|
+
ensureRegisteredPage(page, null);
|
|
174
|
+
registeredPages.get(page).pageOnscreen();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private class RegisteredPage {
|
|
178
|
+
private RegisteredActivity activity;
|
|
179
|
+
List<String> pendingRenders = new ArrayList<String>();
|
|
180
|
+
boolean onScreen = false;
|
|
181
|
+
|
|
182
|
+
private RegisteredPage(RegisteredActivity activity) {
|
|
183
|
+
this.activity = activity;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public RegisteredActivity getPage() {
|
|
187
|
+
return activity;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public void setPage(RegisteredActivity page) {
|
|
191
|
+
this.activity = page;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public void render(String renderJson) {
|
|
195
|
+
if (activity != null && onScreen) {
|
|
196
|
+
activity.render(renderJson);
|
|
197
|
+
} else {
|
|
198
|
+
pendingRenders.add(renderJson);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public String getFieldValue(String field) {
|
|
203
|
+
if (activity != null) {
|
|
204
|
+
return activity.getFieldValue(field);
|
|
205
|
+
} else {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public void pageOffscreen() {
|
|
211
|
+
onScreen = false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public void pageOnscreen() {
|
|
215
|
+
onScreen = true;
|
|
216
|
+
|
|
217
|
+
for (String pendingRender : pendingRenders) {
|
|
218
|
+
Log.d(TAG, "Pending activity for: " + pendingRender);
|
|
219
|
+
|
|
220
|
+
activity.render(pendingRender);
|
|
221
|
+
}
|
|
222
|
+
pendingRenders.clear();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
package com.calatrava.bridge;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.app.AlertDialog;
|
|
5
|
+
import android.content.*;
|
|
6
|
+
import android.os.Bundle;
|
|
7
|
+
import android.os.IBinder;
|
|
8
|
+
import android.util.Log;
|
|
9
|
+
|
|
10
|
+
public abstract class RegisteredActivity extends Activity {
|
|
11
|
+
private String TAG = RegisteredActivity.class.getSimpleName();
|
|
12
|
+
|
|
13
|
+
private RhinoService rhino;
|
|
14
|
+
private RequestLoader spinner = new RequestLoader(this);
|
|
15
|
+
private ServiceConnection connection = new ServiceConnection() {
|
|
16
|
+
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
|
17
|
+
rhino = ((RhinoService.LocalBinder) iBinder).getService();
|
|
18
|
+
RegisteredActivity.this.onRhinoConnected(rhino);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public void onServiceDisconnected(ComponentName componentName) {
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
27
|
+
@Override
|
|
28
|
+
public void onReceive(Context context, Intent intent) {
|
|
29
|
+
Log.d(TAG, "Received broadcast");
|
|
30
|
+
if (intent.getAction().endsWith("start")) {
|
|
31
|
+
spinner.onLoadingStart();
|
|
32
|
+
} else if (intent.getAction().endsWith("finish")) {
|
|
33
|
+
spinner.onLoadingFinish();
|
|
34
|
+
} else {
|
|
35
|
+
AlertDialog.Builder builder = new AlertDialog.Builder(RegisteredActivity.this);
|
|
36
|
+
builder.setMessage(intent.getExtras().getString("message"))
|
|
37
|
+
.setCancelable(false)
|
|
38
|
+
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
|
39
|
+
@Override
|
|
40
|
+
public void onClick(DialogInterface dialogInterface, int i) {
|
|
41
|
+
dialogInterface.dismiss();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
AlertDialog dialog = builder.create();
|
|
45
|
+
dialog.show();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
protected void onCreate(Bundle availableData) {
|
|
52
|
+
super.onCreate(availableData);
|
|
53
|
+
Intent serviceIntent = new Intent(this, RhinoService.class);
|
|
54
|
+
bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Override
|
|
58
|
+
protected void onResume() {
|
|
59
|
+
super.onResume();
|
|
60
|
+
registerReceiver(receiver, new IntentFilter("com.calatrava.ajax.start"));
|
|
61
|
+
registerReceiver(receiver, new IntentFilter("com.calatrava.ajax.finish"));
|
|
62
|
+
registerReceiver(receiver, new IntentFilter("com.calatrava.alert"));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
protected void onPause() {
|
|
67
|
+
super.onPause();
|
|
68
|
+
unregisterReceiver(receiver);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Override
|
|
72
|
+
public void onDestroy() {
|
|
73
|
+
super.onDestroy();
|
|
74
|
+
unbindService(connection);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public void invokeWidgetCallback(String...args) {
|
|
78
|
+
rhino.callJsFunction("tw.bridge.widgets.invokeCallback", args);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected abstract void onRhinoConnected(RhinoService rhino);
|
|
82
|
+
|
|
83
|
+
public abstract String getFieldValue(String field);
|
|
84
|
+
|
|
85
|
+
public abstract void render(final String json);
|
|
86
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.calatrava.bridge;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.app.ActivityManager;
|
|
5
|
+
import android.app.ProgressDialog;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.util.Log;
|
|
8
|
+
|
|
9
|
+
public class RequestLoader {
|
|
10
|
+
private String TAG = RequestLoader.class.getSimpleName();
|
|
11
|
+
|
|
12
|
+
private ProgressDialog dialog;
|
|
13
|
+
private Activity parent;
|
|
14
|
+
|
|
15
|
+
public RequestLoader(Activity parent) {
|
|
16
|
+
this.parent = parent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public void onLoadingStart() {
|
|
20
|
+
Log.d(TAG, "About to create loader");
|
|
21
|
+
dialog = new ProgressDialog(parent);
|
|
22
|
+
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
|
23
|
+
dialog.setMessage("Loading...");
|
|
24
|
+
dialog.setCancelable(false);
|
|
25
|
+
dialog.show();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public void onLoadingFinish() {
|
|
29
|
+
dialog.hide();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
package com.calatrava.bridge;
|
|
2
|
+
|
|
3
|
+
import android.app.Service;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import android.os.*;
|
|
6
|
+
import android.util.Log;
|
|
7
|
+
import org.mozilla.javascript.Context;
|
|
8
|
+
import org.mozilla.javascript.Scriptable;
|
|
9
|
+
import org.mozilla.javascript.ScriptableObject;
|
|
10
|
+
|
|
11
|
+
import java.io.IOException;
|
|
12
|
+
import java.io.Reader;
|
|
13
|
+
import java.lang.String;
|
|
14
|
+
import java.lang.ThreadGroup;
|
|
15
|
+
import java.util.concurrent.CountDownLatch;
|
|
16
|
+
|
|
17
|
+
public class RhinoService extends Service {
|
|
18
|
+
public static String TAG = RhinoService.class.getSimpleName();
|
|
19
|
+
|
|
20
|
+
private Scriptable mScope;
|
|
21
|
+
private JSEvalThread evaller = new JSEvalThread();
|
|
22
|
+
|
|
23
|
+
private final IBinder mBinder = new LocalBinder();
|
|
24
|
+
|
|
25
|
+
CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
26
|
+
|
|
27
|
+
@Override
|
|
28
|
+
public IBinder onBind(Intent intent) {
|
|
29
|
+
return mBinder;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Override
|
|
33
|
+
public void onCreate() {
|
|
34
|
+
super.onCreate();
|
|
35
|
+
Log.d(TAG, "RhinoService created.");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public void initRhino() {
|
|
39
|
+
Context ctxt = enterContext();
|
|
40
|
+
try {
|
|
41
|
+
mScope = ctxt.initStandardObjects();
|
|
42
|
+
|
|
43
|
+
Object wrappedRegistry = Context.javaToJS(PageRegistry.sharedRegistry(), mScope);
|
|
44
|
+
ScriptableObject.putProperty(mScope, "pageRegistry", wrappedRegistry);
|
|
45
|
+
|
|
46
|
+
Object wrappedAjaxRequestManagerRegistry = Context.javaToJS(AjaxRequestManager.sharedManager(), mScope);
|
|
47
|
+
ScriptableObject.putProperty(mScope, "ajaxRequestManagerRegistry", wrappedAjaxRequestManagerRegistry);
|
|
48
|
+
|
|
49
|
+
evaller.start();
|
|
50
|
+
try {
|
|
51
|
+
countDownLatch.await();
|
|
52
|
+
} catch (InterruptedException e) {
|
|
53
|
+
Log.d(TAG, "Interrupted Exception when waiting for JSEvalThread");
|
|
54
|
+
e.printStackTrace();
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
Context.exit();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private Context enterContext() {
|
|
62
|
+
Context ctxt = Context.enter();
|
|
63
|
+
// No pre-compilation
|
|
64
|
+
ctxt.setOptimizationLevel(-1);
|
|
65
|
+
return ctxt;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Override
|
|
69
|
+
public void onDestroy() {
|
|
70
|
+
Log.d(TAG, "RhinoService destroyed.");
|
|
71
|
+
super.onDestroy();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public void load(Reader source, String name) {
|
|
75
|
+
evaller.load(source, name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public void triggerEvent(String page, String eventId, String[] extraArgs) {
|
|
79
|
+
evaller.triggerEvent(page, eventId, extraArgs);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public void invokeSuccessCallback(String requestId, String response) {
|
|
83
|
+
evaller.ajaxSuccessfulResponse(requestId, response);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public void invokeFailureCallback(String requestId, int statusCode, String responseBody) {
|
|
87
|
+
evaller.ajaxFailureResponse(requestId, statusCode, responseBody);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public void callJsFunction(String function) {
|
|
91
|
+
evaller.callJsFunction(function);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public void callJsFunction(String function, String[] args) {
|
|
95
|
+
evaller.callJsFunction(function, args);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public class LocalBinder extends Binder {
|
|
99
|
+
public RhinoService getService() {
|
|
100
|
+
return RhinoService.this;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class JSEvalThread extends Thread {
|
|
105
|
+
private Handler handler;
|
|
106
|
+
private Context ctxt;
|
|
107
|
+
|
|
108
|
+
public JSEvalThread() {
|
|
109
|
+
super(null, null, "js eval thread", 32768);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public void load(final Reader source, final String name) {
|
|
113
|
+
handler.post(new Runnable() {
|
|
114
|
+
@Override
|
|
115
|
+
public void run() {
|
|
116
|
+
try {
|
|
117
|
+
Log.d(TAG, "Loading file: '" + name + "'");
|
|
118
|
+
ctxt.evaluateReader(mScope, source, name, 0, null);
|
|
119
|
+
} catch (IOException e) {
|
|
120
|
+
Log.e(TAG, "Error loading file: '" + name + "'", e);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public void callJsFunction(String function) {
|
|
127
|
+
String js = "{0}();"
|
|
128
|
+
.replace("{0}", function);
|
|
129
|
+
dispatchJs(js);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public void callJsFunction(String function, String[] args) {
|
|
133
|
+
StringBuilder sb = new StringBuilder("");
|
|
134
|
+
boolean first = true;
|
|
135
|
+
for (String arg : args) {
|
|
136
|
+
if (!first) {
|
|
137
|
+
sb.append(",");
|
|
138
|
+
}
|
|
139
|
+
first = false;
|
|
140
|
+
sb.append("'" + arg + "'");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
String js = "{0}({1});"
|
|
144
|
+
.replace("{0}", function)
|
|
145
|
+
.replace("{1}", sb.toString());
|
|
146
|
+
|
|
147
|
+
Log.d(TAG, "Dispatching: " + js);
|
|
148
|
+
|
|
149
|
+
dispatchJs(js);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public void triggerEvent(String page, String eventId, String[] extraArgs) {
|
|
153
|
+
StringBuilder sb = new StringBuilder("");
|
|
154
|
+
for (String arg : extraArgs) {
|
|
155
|
+
sb.append(", '");
|
|
156
|
+
sb.append(arg);
|
|
157
|
+
sb.append("'");
|
|
158
|
+
}
|
|
159
|
+
String js = "tw.bridge.dispatchEvent('{0}', '{1}'{2});"
|
|
160
|
+
.replace("{0}", page)
|
|
161
|
+
.replace("{1}", eventId)
|
|
162
|
+
.replace("{2}", sb.toString());
|
|
163
|
+
|
|
164
|
+
Log.d(TAG, "Dispatching: " + js);
|
|
165
|
+
|
|
166
|
+
dispatchJs(js);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public void ajaxSuccessfulResponse(String requestId, String json) {
|
|
170
|
+
String js = "tw.bridge.requests.successfulResponse('{0}', '{1}');"
|
|
171
|
+
.replace("{0}", requestId)
|
|
172
|
+
.replace("{1}", json);
|
|
173
|
+
dispatchJs(js);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public void ajaxFailureResponse(String requestId, int statusCode, String responseBody) {
|
|
177
|
+
String js = "tw.bridge.requests.failureResponse('{0}', {1}, '{2}');"
|
|
178
|
+
.replace("{0}", requestId)
|
|
179
|
+
.replace("{1}", Integer.toString(statusCode))
|
|
180
|
+
.replace("{2}", responseBody);
|
|
181
|
+
dispatchJs(js);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public void run() {
|
|
185
|
+
Looper.prepare();
|
|
186
|
+
ctxt = enterContext();
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
handler = new Handler();
|
|
190
|
+
countDownLatch.countDown();
|
|
191
|
+
Looper.loop();
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
Context.exit();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private void dispatchJs(final String js) {
|
|
199
|
+
handler.post(new Runnable() {
|
|
200
|
+
@Override
|
|
201
|
+
public void run() {
|
|
202
|
+
eval(js);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private void eval(String jsCode) {
|
|
208
|
+
ctxt.evaluateString(mScope, jsCode, "<Bridge>", 1, null);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
}
|