mobile_template 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +28 -0
  5. data/Rakefile +3 -0
  6. data/bin/mobile_template +71 -0
  7. data/lib/mobile_template/version.rb +4 -0
  8. data/lib/mobile_template.rb +5 -0
  9. data/mobile_template.gemspec +21 -0
  10. data/templates/assets/Gemfile +4 -0
  11. data/templates/assets/config.rb +96 -0
  12. data/templates/assets/config.ru +4 -0
  13. data/templates/assets/source/images/vendor/ajax-loader.gif +0 -0
  14. data/templates/assets/source/images/vendor/ajax-loader.png +0 -0
  15. data/templates/assets/source/images/vendor/icons-18-black.png +0 -0
  16. data/templates/assets/source/images/vendor/icons-18-white.png +0 -0
  17. data/templates/assets/source/images/vendor/icons-36-black.png +0 -0
  18. data/templates/assets/source/images/vendor/icons-36-white.png +0 -0
  19. data/templates/assets/source/index.html.erb +2 -0
  20. data/templates/assets/source/javascripts/app/index.js.coffee +2 -0
  21. data/templates/assets/source/javascripts/application.js.coffee +5 -0
  22. data/templates/assets/source/javascripts/vendor/cordova.js +4841 -0
  23. data/templates/assets/source/javascripts/vendor/jquery.js +9267 -0
  24. data/templates/assets/source/javascripts/vendor/jquery.mobile.js +7410 -0
  25. data/templates/assets/source/layout.erb +24 -0
  26. data/templates/assets/source/stylesheets/application.css.scss +2 -0
  27. data/templates/assets/source/stylesheets/vendor/jquery.mobile.css.scss +1872 -0
  28. data/templates/cordova_android/.gitignore +18 -0
  29. data/templates/cordova_android/LICENSE +202 -0
  30. data/templates/cordova_android/NOTICE +5 -0
  31. data/templates/cordova_android/README.md +95 -0
  32. data/templates/cordova_android/VERSION +1 -0
  33. data/templates/cordova_android/bin/BOOM +4 -0
  34. data/templates/cordova_android/bin/autotest +2 -0
  35. data/templates/cordova_android/bin/bench +29 -0
  36. data/templates/cordova_android/bin/create +46 -0
  37. data/templates/cordova_android/bin/create.bat +1 -0
  38. data/templates/cordova_android/bin/create.js +88 -0
  39. data/templates/cordova_android/bin/create.xml +79 -0
  40. data/templates/cordova_android/bin/node_modules/.bin/cake +7 -0
  41. data/templates/cordova_android/bin/node_modules/.bin/coffee +7 -0
  42. data/templates/cordova_android/bin/node_modules/.bin/nodeunit +120 -0
  43. data/templates/cordova_android/bin/node_modules/coffee-script/.npmignore +11 -0
  44. data/templates/cordova_android/bin/node_modules/coffee-script/LICENSE +22 -0
  45. data/templates/cordova_android/bin/node_modules/coffee-script/README +48 -0
  46. data/templates/cordova_android/bin/node_modules/coffee-script/Rakefile +78 -0
  47. data/templates/cordova_android/bin/node_modules/coffee-script/bin/cake +7 -0
  48. data/templates/cordova_android/bin/node_modules/coffee-script/bin/coffee +7 -0
  49. data/templates/cordova_android/bin/node_modules/coffee-script/extras/jsl.conf +44 -0
  50. data/templates/cordova_android/bin/node_modules/coffee-script/lib/browser.js +75 -0
  51. data/templates/cordova_android/bin/node_modules/coffee-script/lib/cake.js +76 -0
  52. data/templates/cordova_android/bin/node_modules/coffee-script/lib/coffee-script.js +135 -0
  53. data/templates/cordova_android/bin/node_modules/coffee-script/lib/command.js +301 -0
  54. data/templates/cordova_android/bin/node_modules/coffee-script/lib/grammar.js +591 -0
  55. data/templates/cordova_android/bin/node_modules/coffee-script/lib/helpers.js +66 -0
  56. data/templates/cordova_android/bin/node_modules/coffee-script/lib/index.js +8 -0
  57. data/templates/cordova_android/bin/node_modules/coffee-script/lib/lexer.js +656 -0
  58. data/templates/cordova_android/bin/node_modules/coffee-script/lib/nodes.js +2289 -0
  59. data/templates/cordova_android/bin/node_modules/coffee-script/lib/optparse.js +111 -0
  60. data/templates/cordova_android/bin/node_modules/coffee-script/lib/parser.js +676 -0
  61. data/templates/cordova_android/bin/node_modules/coffee-script/lib/repl.js +123 -0
  62. data/templates/cordova_android/bin/node_modules/coffee-script/lib/rewriter.js +363 -0
  63. data/templates/cordova_android/bin/node_modules/coffee-script/lib/scope.js +120 -0
  64. data/templates/cordova_android/bin/node_modules/coffee-script/package.json +27 -0
  65. data/templates/cordova_android/bin/node_modules/nodeunit/.gitignore +5 -0
  66. data/templates/cordova_android/bin/node_modules/nodeunit/.npmignore +3 -0
  67. data/templates/cordova_android/bin/node_modules/nodeunit/CONTRIBUTORS.md +60 -0
  68. data/templates/cordova_android/bin/node_modules/nodeunit/LICENSE +19 -0
  69. data/templates/cordova_android/bin/node_modules/nodeunit/Makefile +126 -0
  70. data/templates/cordova_android/bin/node_modules/nodeunit/README.md +432 -0
  71. data/templates/cordova_android/bin/node_modules/nodeunit/bin/nodeunit +120 -0
  72. data/templates/cordova_android/bin/node_modules/nodeunit/bin/nodeunit.json +10 -0
  73. data/templates/cordova_android/bin/node_modules/nodeunit/deps/async.js +623 -0
  74. data/templates/cordova_android/bin/node_modules/nodeunit/deps/console.log.js +55 -0
  75. data/templates/cordova_android/bin/node_modules/nodeunit/deps/ejs.js +125 -0
  76. data/templates/cordova_android/bin/node_modules/nodeunit/deps/json2.js +483 -0
  77. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/nodeunit.js +1757 -0
  78. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/suite1.js +12 -0
  79. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/suite2.js +13 -0
  80. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/test.html +16 -0
  81. data/templates/cordova_android/bin/node_modules/nodeunit/img/example_fail.png +0 -0
  82. data/templates/cordova_android/bin/node_modules/nodeunit/img/example_pass.png +0 -0
  83. data/templates/cordova_android/bin/node_modules/nodeunit/index.js +3 -0
  84. data/templates/cordova_android/bin/node_modules/nodeunit/lib/assert.js +316 -0
  85. data/templates/cordova_android/bin/node_modules/nodeunit/lib/core.js +260 -0
  86. data/templates/cordova_android/bin/node_modules/nodeunit/lib/nodeunit.js +82 -0
  87. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/browser.js +119 -0
  88. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/default.js +123 -0
  89. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/html.js +107 -0
  90. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/index.js +9 -0
  91. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/junit.js +183 -0
  92. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/minimal.js +112 -0
  93. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/skip_passed.js +105 -0
  94. data/templates/cordova_android/bin/node_modules/nodeunit/lib/track.js +48 -0
  95. data/templates/cordova_android/bin/node_modules/nodeunit/lib/types.js +187 -0
  96. data/templates/cordova_android/bin/node_modules/nodeunit/lib/utils.js +209 -0
  97. data/templates/cordova_android/bin/node_modules/nodeunit/man1/nodeunit.1 +95 -0
  98. data/templates/cordova_android/bin/node_modules/nodeunit/nodelint.cfg +4 -0
  99. data/templates/cordova_android/bin/node_modules/nodeunit/package.json +56 -0
  100. data/templates/cordova_android/bin/node_modules/nodeunit/share/junit.xml.ejs +19 -0
  101. data/templates/cordova_android/bin/node_modules/nodeunit/share/license.js +11 -0
  102. data/templates/cordova_android/bin/node_modules/nodeunit/share/nodeunit.css +70 -0
  103. data/templates/cordova_android/bin/templates/project/.cordova/android/readme.md +1 -0
  104. data/templates/cordova_android/bin/templates/project/.cordova/readme.md +3 -0
  105. data/templates/cordova_android/bin/templates/project/cordova/create +36 -0
  106. data/templates/cordova_android/bin/templates/project/cordova/debug +9 -0
  107. data/templates/cordova_android/bin/templates/project/cordova/emulate +12 -0
  108. data/templates/cordova_android/bin/templates/project/cordova/log +3 -0
  109. data/templates/cordova_android/bin/templates/project/cordova/templates/Activity.java +16 -0
  110. data/templates/cordova_android/bin/templates/project/cordova/templates/project/AndroidManifest.xml +50 -0
  111. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/index.html +42 -0
  112. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/main.js +146 -0
  113. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/master.css +96 -0
  114. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/drawable/icon.png +0 -0
  115. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/xml/cordova.xml +5 -0
  116. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/xml/plugins.xml +19 -0
  117. data/templates/cordova_android/bin/test +26 -0
  118. data/templates/cordova_android/bin/tests/autotest.coffee +4 -0
  119. data/templates/cordova_android/bin/tests/create.coffee +21 -0
  120. data/templates/cordova_android/bin/tests/debug.coffee +0 -0
  121. data/templates/cordova_android/bin/tests/test.coffee +0 -0
  122. data/templates/cordova_android/framework/.classpath +8 -0
  123. data/templates/cordova_android/framework/.project +33 -0
  124. data/templates/cordova_android/framework/AndroidManifest.xml +68 -0
  125. data/templates/cordova_android/framework/ant.properties +34 -0
  126. data/templates/cordova_android/framework/assets/js/accelerometer.js +137 -0
  127. data/templates/cordova_android/framework/assets/js/app.js +89 -0
  128. data/templates/cordova_android/framework/assets/js/battery.js +134 -0
  129. data/templates/cordova_android/framework/assets/js/camera.js +168 -0
  130. data/templates/cordova_android/framework/assets/js/capture.js +203 -0
  131. data/templates/cordova_android/framework/assets/js/compass.js +168 -0
  132. data/templates/cordova_android/framework/assets/js/contact.js +310 -0
  133. data/templates/cordova_android/framework/assets/js/cordova.android.js +4841 -0
  134. data/templates/cordova_android/framework/assets/js/cordova.js.base +924 -0
  135. data/templates/cordova_android/framework/assets/js/crypto.js +54 -0
  136. data/templates/cordova_android/framework/assets/js/device.js +83 -0
  137. data/templates/cordova_android/framework/assets/js/file.js +1082 -0
  138. data/templates/cordova_android/framework/assets/js/filetransfer.js +125 -0
  139. data/templates/cordova_android/framework/assets/js/geolocation.js +209 -0
  140. data/templates/cordova_android/framework/assets/js/header.txt +19 -0
  141. data/templates/cordova_android/framework/assets/js/media.js +233 -0
  142. data/templates/cordova_android/framework/assets/js/network.js +100 -0
  143. data/templates/cordova_android/framework/assets/js/notification.js +133 -0
  144. data/templates/cordova_android/framework/assets/js/position.js +100 -0
  145. data/templates/cordova_android/framework/assets/js/storage.js +439 -0
  146. data/templates/cordova_android/framework/assets/www/index.html +27 -0
  147. data/templates/cordova_android/framework/build.xml +216 -0
  148. data/templates/cordova_android/framework/libs/commons-codec-1.3.jar +0 -0
  149. data/templates/cordova_android/framework/proguard-project.txt +20 -0
  150. data/templates/cordova_android/framework/project.properties +14 -0
  151. data/templates/cordova_android/framework/res/drawable/icon.png +0 -0
  152. data/templates/cordova_android/framework/res/drawable/splash.png +0 -0
  153. data/templates/cordova_android/framework/res/layout/main.xml +29 -0
  154. data/templates/cordova_android/framework/res/values/strings.xml +23 -0
  155. data/templates/cordova_android/framework/res/xml/cordova.xml +37 -0
  156. data/templates/cordova_android/framework/res/xml/plugins.xml +37 -0
  157. data/templates/cordova_android/framework/src/com/phonegap/api/IPlugin.java +27 -0
  158. data/templates/cordova_android/framework/src/com/phonegap/api/LOG.java +28 -0
  159. data/templates/cordova_android/framework/src/com/phonegap/api/PhonegapActivity.java +28 -0
  160. data/templates/cordova_android/framework/src/com/phonegap/api/Plugin.java +27 -0
  161. data/templates/cordova_android/framework/src/com/phonegap/api/PluginManager.java +35 -0
  162. data/templates/cordova_android/framework/src/com/phonegap/api/PluginResult.java +53 -0
  163. data/templates/cordova_android/framework/src/org/apache/cordova/AccelListener.java +311 -0
  164. data/templates/cordova_android/framework/src/org/apache/cordova/App.java +198 -0
  165. data/templates/cordova_android/framework/src/org/apache/cordova/AudioHandler.java +364 -0
  166. data/templates/cordova_android/framework/src/org/apache/cordova/AudioPlayer.java +450 -0
  167. data/templates/cordova_android/framework/src/org/apache/cordova/AuthenticationToken.java +69 -0
  168. data/templates/cordova_android/framework/src/org/apache/cordova/BatteryListener.java +156 -0
  169. data/templates/cordova_android/framework/src/org/apache/cordova/CallbackServer.java +431 -0
  170. data/templates/cordova_android/framework/src/org/apache/cordova/CameraLauncher.java +500 -0
  171. data/templates/cordova_android/framework/src/org/apache/cordova/Capture.java +400 -0
  172. data/templates/cordova_android/framework/src/org/apache/cordova/CompassListener.java +308 -0
  173. data/templates/cordova_android/framework/src/org/apache/cordova/ContactAccessor.java +198 -0
  174. data/templates/cordova_android/framework/src/org/apache/cordova/ContactAccessorSdk5.java +1934 -0
  175. data/templates/cordova_android/framework/src/org/apache/cordova/ContactManager.java +113 -0
  176. data/templates/cordova_android/framework/src/org/apache/cordova/CordovaChromeClient.java +314 -0
  177. data/templates/cordova_android/framework/src/org/apache/cordova/CordovaWebViewClient.java +306 -0
  178. data/templates/cordova_android/framework/src/org/apache/cordova/Device.java +219 -0
  179. data/templates/cordova_android/framework/src/org/apache/cordova/DirectoryManager.java +161 -0
  180. data/templates/cordova_android/framework/src/org/apache/cordova/DroidGap.java +1417 -0
  181. data/templates/cordova_android/framework/src/org/apache/cordova/ExifHelper.java +165 -0
  182. data/templates/cordova_android/framework/src/org/apache/cordova/FileTransfer.java +458 -0
  183. data/templates/cordova_android/framework/src/org/apache/cordova/FileUploadResult.java +63 -0
  184. data/templates/cordova_android/framework/src/org/apache/cordova/FileUtils.java +1048 -0
  185. data/templates/cordova_android/framework/src/org/apache/cordova/GeoBroker.java +165 -0
  186. data/templates/cordova_android/framework/src/org/apache/cordova/GeoListener.java +133 -0
  187. data/templates/cordova_android/framework/src/org/apache/cordova/GpsListener.java +163 -0
  188. data/templates/cordova_android/framework/src/org/apache/cordova/HttpHandler.java +80 -0
  189. data/templates/cordova_android/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java +104 -0
  190. data/templates/cordova_android/framework/src/org/apache/cordova/NetworkListener.java +153 -0
  191. data/templates/cordova_android/framework/src/org/apache/cordova/NetworkManager.java +248 -0
  192. data/templates/cordova_android/framework/src/org/apache/cordova/Notification.java +366 -0
  193. data/templates/cordova_android/framework/src/org/apache/cordova/PreferenceNode.java +34 -0
  194. data/templates/cordova_android/framework/src/org/apache/cordova/PreferenceSet.java +62 -0
  195. data/templates/cordova_android/framework/src/org/apache/cordova/StandAlone.java +35 -0
  196. data/templates/cordova_android/framework/src/org/apache/cordova/Storage.java +239 -0
  197. data/templates/cordova_android/framework/src/org/apache/cordova/TempListener.java +112 -0
  198. data/templates/cordova_android/framework/src/org/apache/cordova/api/CordovaInterface.java +145 -0
  199. data/templates/cordova_android/framework/src/org/apache/cordova/api/IPlugin.java +116 -0
  200. data/templates/cordova_android/framework/src/org/apache/cordova/api/LOG.java +234 -0
  201. data/templates/cordova_android/framework/src/org/apache/cordova/api/Plugin.java +210 -0
  202. data/templates/cordova_android/framework/src/org/apache/cordova/api/PluginManager.java +359 -0
  203. data/templates/cordova_android/framework/src/org/apache/cordova/api/PluginResult.java +119 -0
  204. data/templates/cordova_android/framework/src/org/apache/cordova/file/EncodingException.java +28 -0
  205. data/templates/cordova_android/framework/src/org/apache/cordova/file/FileExistsException.java +28 -0
  206. data/templates/cordova_android/framework/src/org/apache/cordova/file/InvalidModificationException.java +29 -0
  207. data/templates/cordova_android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java +28 -0
  208. data/templates/cordova_android/framework/src/org/apache/cordova/file/TypeMismatchException.java +29 -0
  209. data/templates/cordova_android/framework/test/org/apache/cordova/PreferenceNodeTest.java +53 -0
  210. data/templates/cordova_android/framework/test/org/apache/cordova/PreferenceSetTest.java +73 -0
  211. metadata +279 -0
@@ -0,0 +1,1417 @@
1
+ /*
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ */
19
+ package org.apache.cordova;
20
+
21
+ import java.io.IOException;
22
+ import java.util.ArrayList;
23
+ import java.util.HashMap;
24
+ import java.util.Hashtable;
25
+ import java.util.Iterator;
26
+ import java.util.Stack;
27
+ import java.util.regex.Matcher;
28
+ import java.util.regex.Pattern;
29
+
30
+ import org.apache.cordova.PreferenceNode;
31
+ import org.apache.cordova.PreferenceSet;
32
+ import org.apache.cordova.api.IPlugin;
33
+ import org.apache.cordova.api.LOG;
34
+ import org.apache.cordova.api.CordovaInterface;
35
+ import org.apache.cordova.api.PluginManager;
36
+ import org.xmlpull.v1.XmlPullParserException;
37
+
38
+ import android.app.Activity;
39
+ import android.app.AlertDialog;
40
+ import android.app.ProgressDialog;
41
+ import android.content.Context;
42
+ import android.content.DialogInterface;
43
+ import android.content.Intent;
44
+ import android.content.res.Configuration;
45
+ import android.content.res.XmlResourceParser;
46
+ import android.database.Cursor;
47
+ import android.graphics.Color;
48
+ import android.media.AudioManager;
49
+ import android.net.Uri;
50
+ import android.os.Bundle;
51
+ import android.view.Display;
52
+ import android.view.KeyEvent;
53
+ import android.view.Menu;
54
+ import android.view.MenuItem;
55
+ import android.view.View;
56
+ import android.view.ViewGroup;
57
+ import android.view.Window;
58
+ import android.view.WindowManager;
59
+ import android.webkit.WebSettings;
60
+ import android.webkit.WebSettings.LayoutAlgorithm;
61
+ import android.webkit.WebView;
62
+ import android.webkit.WebViewClient;
63
+ import android.widget.LinearLayout;
64
+
65
+
66
+
67
+ /**
68
+ * This class is the main Android activity that represents the Cordova
69
+ * application. It should be extended by the user to load the specific
70
+ * html file that contains the application.
71
+ *
72
+ * As an example:
73
+ *
74
+ * package org.apache.cordova.examples;
75
+ * import android.app.Activity;
76
+ * import android.os.Bundle;
77
+ * import org.apache.cordova.*;
78
+ *
79
+ * public class Examples extends DroidGap {
80
+ * @Override
81
+ * public void onCreate(Bundle savedInstanceState) {
82
+ * super.onCreate(savedInstanceState);
83
+ *
84
+ * // Set properties for activity
85
+ * super.setStringProperty("loadingDialog", "Title,Message"); // show loading dialog
86
+ * super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
87
+ *
88
+ * // Initialize activity
89
+ * super.init();
90
+ *
91
+ * // Clear cache if you want
92
+ * super.appView.clearCache(true);
93
+ *
94
+ * // Load your application
95
+ * super.setIntegerProperty("splashscreen", R.drawable.splash); // load splash.jpg image from the resource drawable directory
96
+ * super.loadUrl("file:///android_asset/www/index.html", 3000); // show splash screen 3 sec before loading app
97
+ * }
98
+ * }
99
+ *
100
+ * Properties: The application can be configured using the following properties:
101
+ *
102
+ * // Display a native loading dialog when loading app. Format for value = "Title,Message".
103
+ * // (String - default=null)
104
+ * super.setStringProperty("loadingDialog", "Wait,Loading Demo...");
105
+ *
106
+ * // Display a native loading dialog when loading sub-pages. Format for value = "Title,Message".
107
+ * // (String - default=null)
108
+ * super.setStringProperty("loadingPageDialog", "Loading page...");
109
+ *
110
+ * // Load a splash screen image from the resource drawable directory.
111
+ * // (Integer - default=0)
112
+ * super.setIntegerProperty("splashscreen", R.drawable.splash);
113
+ *
114
+ * // Set the background color.
115
+ * // (Integer - default=0 or BLACK)
116
+ * super.setIntegerProperty("backgroundColor", Color.WHITE);
117
+ *
118
+ * // Time in msec to wait before triggering a timeout error when loading
119
+ * // with super.loadUrl(). (Integer - default=20000)
120
+ * super.setIntegerProperty("loadUrlTimeoutValue", 60000);
121
+ *
122
+ * // URL to load if there's an error loading specified URL with loadUrl().
123
+ * // Should be a local URL starting with file://. (String - default=null)
124
+ * super.setStringProperty("errorUrl", "file:///android_asset/www/error.html");
125
+ *
126
+ * // Enable app to keep running in background. (Boolean - default=true)
127
+ * super.setBooleanProperty("keepRunning", false);
128
+ *
129
+ * Cordova.xml configuration:
130
+ * Cordova uses a configuration file at res/xml/cordova.xml to specify the following settings.
131
+ *
132
+ * Approved list of URLs that can be loaded into DroidGap
133
+ * <access origin="http://server regexp" subdomains="true" />
134
+ * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
135
+ * <log level="DEBUG" />
136
+ *
137
+ * Cordova plugins:
138
+ * Cordova uses a file at res/xml/plugins.xml to list all plugins that are installed.
139
+ * Before using a new plugin, a new element must be added to the file.
140
+ * name attribute is the service name passed to Cordova.exec() in JavaScript
141
+ * value attribute is the Java class name to call.
142
+ *
143
+ * <plugins>
144
+ * <plugin name="App" value="org.apache.cordova.App"/>
145
+ * ...
146
+ * </plugins>
147
+ */
148
+ public class DroidGap extends Activity implements CordovaInterface {
149
+ public static String TAG = "DroidGap";
150
+
151
+ // The webview for our app
152
+ protected WebView appView;
153
+ protected WebViewClient webViewClient;
154
+ private ArrayList<Pattern> whiteList = new ArrayList<Pattern>();
155
+ private HashMap<String, Boolean> whiteListCache = new HashMap<String,Boolean>();
156
+
157
+ protected LinearLayout root;
158
+ public boolean bound = false;
159
+ public CallbackServer callbackServer;
160
+ protected PluginManager pluginManager;
161
+ protected boolean cancelLoadUrl = false;
162
+ protected ProgressDialog spinnerDialog = null;
163
+
164
+ // The initial URL for our app
165
+ // ie http://server/path/index.html#abc?query
166
+ private String url = null;
167
+ private Stack<String> urls = new Stack<String>();
168
+
169
+ // Url was specified from extras (activity was started programmatically)
170
+ private String initUrl = null;
171
+
172
+ private static int ACTIVITY_STARTING = 0;
173
+ private static int ACTIVITY_RUNNING = 1;
174
+ private static int ACTIVITY_EXITING = 2;
175
+ private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
176
+
177
+ // The base of the initial URL for our app.
178
+ // Does not include file name. Ends with /
179
+ // ie http://server/path/
180
+ String baseUrl = null;
181
+
182
+ // Plugin to call when activity result is received
183
+ protected IPlugin activityResultCallback = null;
184
+ protected boolean activityResultKeepRunning;
185
+
186
+ // Flag indicates that a loadUrl timeout occurred
187
+ int loadUrlTimeout = 0;
188
+
189
+ // Default background color for activity
190
+ // (this is not the color for the webview, which is set in HTML)
191
+ private int backgroundColor = Color.BLACK;
192
+
193
+ /** The authorization tokens. */
194
+ private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
195
+
196
+ /*
197
+ * The variables below are used to cache some of the activity properties.
198
+ */
199
+
200
+ // Draw a splash screen using an image located in the drawable resource directory.
201
+ // This is not the same as calling super.loadSplashscreen(url)
202
+ protected int splashscreen = 0;
203
+
204
+ // LoadUrl timeout value in msec (default of 20 sec)
205
+ protected int loadUrlTimeoutValue = 20000;
206
+
207
+ // Keep app running when pause is received. (default = true)
208
+ // If true, then the JavaScript and native code continue to run in the background
209
+ // when another application (activity) is started.
210
+ protected boolean keepRunning = true;
211
+
212
+ // preferences read from cordova.xml
213
+ protected PreferenceSet preferences;
214
+
215
+ /**
216
+ * Sets the authentication token.
217
+ *
218
+ * @param authenticationToken
219
+ * the authentication token
220
+ * @param host
221
+ * the host
222
+ * @param realm
223
+ * the realm
224
+ */
225
+ public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
226
+
227
+ if(host == null) {
228
+ host = "";
229
+ }
230
+
231
+ if(realm == null) {
232
+ realm = "";
233
+ }
234
+
235
+ authenticationTokens.put(host.concat(realm), authenticationToken);
236
+ }
237
+
238
+ /**
239
+ * Removes the authentication token.
240
+ *
241
+ * @param host
242
+ * the host
243
+ * @param realm
244
+ * the realm
245
+ * @return the authentication token or null if did not exist
246
+ */
247
+ public AuthenticationToken removeAuthenticationToken(String host, String realm) {
248
+ return authenticationTokens.remove(host.concat(realm));
249
+ }
250
+
251
+ /**
252
+ * Gets the authentication token.
253
+ *
254
+ * In order it tries:
255
+ * 1- host + realm
256
+ * 2- host
257
+ * 3- realm
258
+ * 4- no host, no realm
259
+ *
260
+ * @param host
261
+ * the host
262
+ * @param realm
263
+ * the realm
264
+ * @return the authentication token
265
+ */
266
+ public AuthenticationToken getAuthenticationToken(String host, String realm) {
267
+ AuthenticationToken token = null;
268
+
269
+ token = authenticationTokens.get(host.concat(realm));
270
+
271
+ if(token == null) {
272
+ // try with just the host
273
+ token = authenticationTokens.get(host);
274
+
275
+ // Try the realm
276
+ if(token == null) {
277
+ token = authenticationTokens.get(realm);
278
+ }
279
+
280
+ // if no host found, just query for default
281
+ if(token == null) {
282
+ token = authenticationTokens.get("");
283
+ }
284
+ }
285
+
286
+ return token;
287
+ }
288
+
289
+ /**
290
+ * Clear all authentication tokens.
291
+ */
292
+ public void clearAuthenticationTokens() {
293
+ authenticationTokens.clear();
294
+ }
295
+
296
+
297
+ /**
298
+ * Called when the activity is first created.
299
+ *
300
+ * @param savedInstanceState
301
+ */
302
+ @Override
303
+ public void onCreate(Bundle savedInstanceState) {
304
+ preferences = new PreferenceSet();
305
+
306
+ // Load Cordova configuration:
307
+ // white list of allowed URLs
308
+ // debug setting
309
+ this.loadConfiguration();
310
+
311
+ LOG.d(TAG, "DroidGap.onCreate()");
312
+ super.onCreate(savedInstanceState);
313
+
314
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
315
+
316
+ if (preferences.prefMatches("fullscreen","true")) {
317
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
318
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
319
+ } else {
320
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
321
+ WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
322
+ }
323
+
324
+ // This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
325
+ Display display = getWindowManager().getDefaultDisplay();
326
+ int width = display.getWidth();
327
+ int height = display.getHeight();
328
+
329
+ root = new LinearLayoutSoftKeyboardDetect(this, width, height);
330
+ root.setOrientation(LinearLayout.VERTICAL);
331
+ root.setBackgroundColor(this.backgroundColor);
332
+ root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
333
+ ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
334
+
335
+ // If url was passed in to intent, then init webview, which will load the url
336
+ Bundle bundle = this.getIntent().getExtras();
337
+ if (bundle != null) {
338
+ String url = bundle.getString("url");
339
+ if (url != null) {
340
+ this.initUrl = url;
341
+ }
342
+ }
343
+ // Setup the hardware volume controls to handle volume control
344
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
345
+ }
346
+
347
+ /**
348
+ * Create and initialize web container.
349
+ */
350
+ public void init() {
351
+ LOG.d(TAG, "DroidGap.init()");
352
+
353
+ // Create web container
354
+ this.appView = new WebView(DroidGap.this);
355
+ this.appView.setId(100);
356
+
357
+ this.appView.setLayoutParams(new LinearLayout.LayoutParams(
358
+ ViewGroup.LayoutParams.FILL_PARENT,
359
+ ViewGroup.LayoutParams.FILL_PARENT,
360
+ 1.0F));
361
+
362
+ this.appView.setWebChromeClient(new CordovaChromeClient(DroidGap.this));
363
+ this.setWebViewClient(this.appView, new CordovaWebViewClient(this));
364
+
365
+ this.appView.setInitialScale(0);
366
+ this.appView.setVerticalScrollBarEnabled(false);
367
+ this.appView.requestFocusFromTouch();
368
+
369
+ // Enable JavaScript
370
+ WebSettings settings = this.appView.getSettings();
371
+ settings.setJavaScriptEnabled(true);
372
+ settings.setJavaScriptCanOpenWindowsAutomatically(true);
373
+ settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
374
+
375
+ //Set the nav dump for HTC
376
+ settings.setNavDump(true);
377
+
378
+ // Enable database
379
+ settings.setDatabaseEnabled(true);
380
+ String databasePath = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
381
+ settings.setDatabasePath(databasePath);
382
+
383
+ // Enable DOM storage
384
+ settings.setDomStorageEnabled(true);
385
+
386
+ // Enable built-in geolocation
387
+ settings.setGeolocationEnabled(true);
388
+
389
+ // Add web view but make it invisible while loading URL
390
+ this.appView.setVisibility(View.INVISIBLE);
391
+ root.addView(this.appView);
392
+ setContentView(root);
393
+
394
+ // Clear cancel flag
395
+ this.cancelLoadUrl = false;
396
+ }
397
+
398
+ /**
399
+ * Set the WebViewClient.
400
+ *
401
+ * @param appView
402
+ * @param client
403
+ */
404
+ protected void setWebViewClient(WebView appView, WebViewClient client) {
405
+ this.webViewClient = client;
406
+ appView.setWebViewClient(client);
407
+ }
408
+
409
+ /**
410
+ * Look at activity parameters and process them.
411
+ * This must be called from the main UI thread.
412
+ */
413
+ private void handleActivityParameters() {
414
+
415
+ // If backgroundColor
416
+ this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
417
+ this.root.setBackgroundColor(this.backgroundColor);
418
+
419
+ // If spashscreen
420
+ this.splashscreen = this.getIntegerProperty("splashscreen", 0);
421
+ if ((this.urls.size() == 0) && (this.splashscreen != 0)) {
422
+ root.setBackgroundResource(this.splashscreen);
423
+ }
424
+
425
+ // If loadUrlTimeoutValue
426
+ int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0);
427
+ if (timeout > 0) {
428
+ this.loadUrlTimeoutValue = timeout;
429
+ }
430
+
431
+ // If keepRunning
432
+ this.keepRunning = this.getBooleanProperty("keepRunning", true);
433
+ }
434
+
435
+ /**
436
+ * Load the url into the webview.
437
+ *
438
+ * @param url
439
+ */
440
+ public void loadUrl(String url) {
441
+
442
+ // If first page of app, then set URL to load to be the one passed in
443
+ if (this.initUrl == null || (this.urls.size() > 0)) {
444
+ this.loadUrlIntoView(url);
445
+ }
446
+ // Otherwise use the URL specified in the activity's extras bundle
447
+ else {
448
+ this.loadUrlIntoView(this.initUrl);
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Load the url into the webview.
454
+ *
455
+ * @param url
456
+ */
457
+ private void loadUrlIntoView(final String url) {
458
+ if (!url.startsWith("javascript:")) {
459
+ LOG.d(TAG, "DroidGap.loadUrl(%s)", url);
460
+ }
461
+
462
+ this.url = url;
463
+ if (this.baseUrl == null) {
464
+ int i = url.lastIndexOf('/');
465
+ if (i > 0) {
466
+ this.baseUrl = url.substring(0, i+1);
467
+ }
468
+ else {
469
+ this.baseUrl = this.url + "/";
470
+ }
471
+ }
472
+ if (!url.startsWith("javascript:")) {
473
+ LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl);
474
+ }
475
+
476
+ // Load URL on UI thread
477
+ final DroidGap me = this;
478
+ this.runOnUiThread(new Runnable() {
479
+ public void run() {
480
+
481
+ // Init web view if not already done
482
+ if (me.appView == null) {
483
+ me.init();
484
+ }
485
+
486
+ // Handle activity parameters
487
+ me.handleActivityParameters();
488
+
489
+ // Track URLs loaded instead of using appView history
490
+ me.urls.push(url);
491
+ me.appView.clearHistory();
492
+
493
+ // Create callback server and plugin manager
494
+ if (me.callbackServer == null) {
495
+ me.callbackServer = new CallbackServer();
496
+ me.callbackServer.init(url);
497
+ }
498
+ else {
499
+ me.callbackServer.reinit(url);
500
+ }
501
+ if (me.pluginManager == null) {
502
+ me.pluginManager = new PluginManager(me.appView, me);
503
+ }
504
+ else {
505
+ me.pluginManager.reinit();
506
+ }
507
+
508
+ // If loadingDialog property, then show the App loading dialog for first page of app
509
+ String loading = null;
510
+ if (me.urls.size() == 1) {
511
+ loading = me.getStringProperty("loadingDialog", null);
512
+ }
513
+ else {
514
+ loading = me.getStringProperty("loadingPageDialog", null);
515
+ }
516
+ if (loading != null) {
517
+
518
+ String title = "";
519
+ String message = "Loading Application...";
520
+
521
+ if (loading.length() > 0) {
522
+ int comma = loading.indexOf(',');
523
+ if (comma > 0) {
524
+ title = loading.substring(0, comma);
525
+ message = loading.substring(comma+1);
526
+ }
527
+ else {
528
+ title = "";
529
+ message = loading;
530
+ }
531
+ }
532
+ me.spinnerStart(title, message);
533
+ }
534
+
535
+ // Create a timeout timer for loadUrl
536
+ final int currentLoadUrlTimeout = me.loadUrlTimeout;
537
+ Runnable runnable = new Runnable() {
538
+ public void run() {
539
+ try {
540
+ synchronized(this) {
541
+ wait(me.loadUrlTimeoutValue);
542
+ }
543
+ } catch (InterruptedException e) {
544
+ e.printStackTrace();
545
+ }
546
+
547
+ // If timeout, then stop loading and handle error
548
+ if (me.loadUrlTimeout == currentLoadUrlTimeout) {
549
+ me.appView.stopLoading();
550
+ LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient");
551
+ me.webViewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url);
552
+ }
553
+ }
554
+ };
555
+ Thread thread = new Thread(runnable);
556
+ thread.start();
557
+ me.appView.loadUrl(url);
558
+ }
559
+ });
560
+ }
561
+
562
+ /**
563
+ * Load the url into the webview after waiting for period of time.
564
+ * This is used to display the splashscreen for certain amount of time.
565
+ *
566
+ * @param url
567
+ * @param time The number of ms to wait before loading webview
568
+ */
569
+ public void loadUrl(final String url, int time) {
570
+
571
+ // If first page of app, then set URL to load to be the one passed in
572
+ if (this.initUrl == null || (this.urls.size() > 0)) {
573
+ this.loadUrlIntoView(url, time);
574
+ }
575
+ // Otherwise use the URL specified in the activity's extras bundle
576
+ else {
577
+ this.loadUrlIntoView(this.initUrl);
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Load the url into the webview after waiting for period of time.
583
+ * This is used to display the splashscreen for certain amount of time.
584
+ *
585
+ * @param url
586
+ * @param time The number of ms to wait before loading webview
587
+ */
588
+ private void loadUrlIntoView(final String url, final int time) {
589
+
590
+ // Clear cancel flag
591
+ this.cancelLoadUrl = false;
592
+
593
+ // If not first page of app, then load immediately
594
+ if (this.urls.size() > 0) {
595
+ this.loadUrlIntoView(url);
596
+ }
597
+
598
+ if (!url.startsWith("javascript:")) {
599
+ LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
600
+ }
601
+ final DroidGap me = this;
602
+
603
+ // Handle activity parameters
604
+ this.runOnUiThread(new Runnable() {
605
+ public void run() {
606
+ if (me.appView == null) {
607
+ me.init();
608
+ }
609
+ me.handleActivityParameters();
610
+ }
611
+ });
612
+
613
+ Runnable runnable = new Runnable() {
614
+ public void run() {
615
+ try {
616
+ synchronized(this) {
617
+ this.wait(time);
618
+ }
619
+ } catch (InterruptedException e) {
620
+ e.printStackTrace();
621
+ }
622
+ if (!me.cancelLoadUrl) {
623
+ me.loadUrlIntoView(url);
624
+ }
625
+ else{
626
+ me.cancelLoadUrl = false;
627
+ LOG.d(TAG, "Aborting loadUrl(%s): Another URL was loaded before timer expired.", url);
628
+ }
629
+ }
630
+ };
631
+ Thread thread = new Thread(runnable);
632
+ thread.start();
633
+ }
634
+
635
+ /**
636
+ * Cancel loadUrl before it has been loaded.
637
+ */
638
+ public void cancelLoadUrl() {
639
+ this.cancelLoadUrl = true;
640
+ }
641
+
642
+ /**
643
+ * Clear the resource cache.
644
+ */
645
+ public void clearCache() {
646
+ if (this.appView == null) {
647
+ this.init();
648
+ }
649
+ this.appView.clearCache(true);
650
+ }
651
+
652
+ /**
653
+ * Clear web history in this web view.
654
+ */
655
+ public void clearHistory() {
656
+ this.urls.clear();
657
+ this.appView.clearHistory();
658
+
659
+ // Leave current url on history stack
660
+ if (this.url != null) {
661
+ this.urls.push(this.url);
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Go to previous page in history. (We manage our own history)
667
+ *
668
+ * @return true if we went back, false if we are already at top
669
+ */
670
+ public boolean backHistory() {
671
+
672
+ // Check webview first to see if there is a history
673
+ // This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
674
+ if (this.appView.canGoBack()) {
675
+ this.appView.goBack();
676
+ return true;
677
+ }
678
+
679
+ // If our managed history has prev url
680
+ if (this.urls.size() > 1) {
681
+ this.urls.pop(); // Pop current url
682
+ String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
683
+ this.loadUrl(url);
684
+ return true;
685
+ }
686
+
687
+ return false;
688
+ }
689
+
690
+ @Override
691
+ /**
692
+ * Called by the system when the device configuration changes while your activity is running.
693
+ *
694
+ * @param Configuration newConfig
695
+ */
696
+ public void onConfigurationChanged(Configuration newConfig) {
697
+ //don't reload the current page when the orientation is changed
698
+ super.onConfigurationChanged(newConfig);
699
+ }
700
+
701
+ /**
702
+ * Get boolean property for activity.
703
+ *
704
+ * @param name
705
+ * @param defaultValue
706
+ * @return
707
+ */
708
+ public boolean getBooleanProperty(String name, boolean defaultValue) {
709
+ Bundle bundle = this.getIntent().getExtras();
710
+ if (bundle == null) {
711
+ return defaultValue;
712
+ }
713
+ Boolean p = (Boolean)bundle.get(name);
714
+ if (p == null) {
715
+ return defaultValue;
716
+ }
717
+ return p.booleanValue();
718
+ }
719
+
720
+ /**
721
+ * Get int property for activity.
722
+ *
723
+ * @param name
724
+ * @param defaultValue
725
+ * @return
726
+ */
727
+ public int getIntegerProperty(String name, int defaultValue) {
728
+ Bundle bundle = this.getIntent().getExtras();
729
+ if (bundle == null) {
730
+ return defaultValue;
731
+ }
732
+ Integer p = (Integer)bundle.get(name);
733
+ if (p == null) {
734
+ return defaultValue;
735
+ }
736
+ return p.intValue();
737
+ }
738
+
739
+ /**
740
+ * Get string property for activity.
741
+ *
742
+ * @param name
743
+ * @param defaultValue
744
+ * @return
745
+ */
746
+ public String getStringProperty(String name, String defaultValue) {
747
+ Bundle bundle = this.getIntent().getExtras();
748
+ if (bundle == null) {
749
+ return defaultValue;
750
+ }
751
+ String p = bundle.getString(name);
752
+ if (p == null) {
753
+ return defaultValue;
754
+ }
755
+ return p;
756
+ }
757
+
758
+ /**
759
+ * Get double property for activity.
760
+ *
761
+ * @param name
762
+ * @param defaultValue
763
+ * @return
764
+ */
765
+ public double getDoubleProperty(String name, double defaultValue) {
766
+ Bundle bundle = this.getIntent().getExtras();
767
+ if (bundle == null) {
768
+ return defaultValue;
769
+ }
770
+ Double p = (Double)bundle.get(name);
771
+ if (p == null) {
772
+ return defaultValue;
773
+ }
774
+ return p.doubleValue();
775
+ }
776
+
777
+ /**
778
+ * Set boolean property on activity.
779
+ *
780
+ * @param name
781
+ * @param value
782
+ */
783
+ public void setBooleanProperty(String name, boolean value) {
784
+ this.getIntent().putExtra(name, value);
785
+ }
786
+
787
+ /**
788
+ * Set int property on activity.
789
+ *
790
+ * @param name
791
+ * @param value
792
+ */
793
+ public void setIntegerProperty(String name, int value) {
794
+ this.getIntent().putExtra(name, value);
795
+ }
796
+
797
+ /**
798
+ * Set string property on activity.
799
+ *
800
+ * @param name
801
+ * @param value
802
+ */
803
+ public void setStringProperty(String name, String value) {
804
+ this.getIntent().putExtra(name, value);
805
+ }
806
+
807
+ /**
808
+ * Set double property on activity.
809
+ *
810
+ * @param name
811
+ * @param value
812
+ */
813
+ public void setDoubleProperty(String name, double value) {
814
+ this.getIntent().putExtra(name, value);
815
+ }
816
+
817
+ @Override
818
+ /**
819
+ * Called when the system is about to start resuming a previous activity.
820
+ */
821
+ protected void onPause() {
822
+ super.onPause();
823
+
824
+ // Don't process pause if shutting down, since onDestroy() will be called
825
+ if (this.activityState == ACTIVITY_EXITING) {
826
+ return;
827
+ }
828
+
829
+ if (this.appView == null) {
830
+ return;
831
+ }
832
+
833
+ // Send pause event to JavaScript
834
+ this.appView.loadUrl("javascript:try{require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};");
835
+
836
+ // Forward to plugins
837
+ this.pluginManager.onPause(this.keepRunning);
838
+
839
+ // If app doesn't want to run in background
840
+ if (!this.keepRunning) {
841
+
842
+ // Pause JavaScript timers (including setInterval)
843
+ this.appView.pauseTimers();
844
+ }
845
+ }
846
+
847
+ @Override
848
+ /**
849
+ * Called when the activity receives a new intent
850
+ **/
851
+ protected void onNewIntent(Intent intent) {
852
+ super.onNewIntent(intent);
853
+
854
+ //Forward to plugins
855
+ this.pluginManager.onNewIntent(intent);
856
+ }
857
+
858
+ @Override
859
+ /**
860
+ * Called when the activity will start interacting with the user.
861
+ */
862
+ protected void onResume() {
863
+ super.onResume();
864
+
865
+ if (this.activityState == ACTIVITY_STARTING) {
866
+ this.activityState = ACTIVITY_RUNNING;
867
+ return;
868
+ }
869
+
870
+ if (this.appView == null) {
871
+ return;
872
+ }
873
+
874
+ // Send resume event to JavaScript
875
+ this.appView.loadUrl("javascript:try{require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};");
876
+
877
+ // Forward to plugins
878
+ this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
879
+
880
+ // If app doesn't want to run in background
881
+ if (!this.keepRunning || this.activityResultKeepRunning) {
882
+
883
+ // Restore multitasking state
884
+ if (this.activityResultKeepRunning) {
885
+ this.keepRunning = this.activityResultKeepRunning;
886
+ this.activityResultKeepRunning = false;
887
+ }
888
+
889
+ // Resume JavaScript timers (including setInterval)
890
+ this.appView.resumeTimers();
891
+ }
892
+ }
893
+
894
+ @Override
895
+ /**
896
+ * The final call you receive before your activity is destroyed.
897
+ */
898
+ public void onDestroy() {
899
+ super.onDestroy();
900
+
901
+ if (this.appView != null) {
902
+
903
+
904
+ // Send destroy event to JavaScript
905
+ this.appView.loadUrl("javascript:try{require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
906
+
907
+ // Load blank page so that JavaScript onunload is called
908
+ this.appView.loadUrl("about:blank");
909
+
910
+ // Forward to plugins
911
+ if (this.pluginManager != null) {
912
+ this.pluginManager.onDestroy();
913
+ }
914
+ }
915
+ else {
916
+ this.endActivity();
917
+ }
918
+ }
919
+
920
+ /**
921
+ * Send a message to all plugins.
922
+ *
923
+ * @param id The message id
924
+ * @param data The message data
925
+ */
926
+ public void postMessage(String id, Object data) {
927
+
928
+ // Forward to plugins
929
+ if (this.pluginManager != null) {
930
+ this.pluginManager.postMessage(id, data);
931
+ }
932
+ }
933
+
934
+ /**
935
+ * @deprecated
936
+ * Add services to res/xml/plugins.xml instead.
937
+ *
938
+ * Add a class that implements a service.
939
+ *
940
+ * @param serviceType
941
+ * @param className
942
+ */
943
+ @Deprecated
944
+ public void addService(String serviceType, String className) {
945
+ this.pluginManager.addService(serviceType, className);
946
+ }
947
+
948
+ /**
949
+ * Send JavaScript statement back to JavaScript.
950
+ * (This is a convenience method)
951
+ *
952
+ * @param message
953
+ */
954
+ public void sendJavascript(String statement) {
955
+ this.callbackServer.sendJavascript(statement);
956
+ }
957
+
958
+ /**
959
+ * Load the specified URL in the Cordova webview or a new browser instance.
960
+ *
961
+ * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
962
+ *
963
+ * @param url The url to load.
964
+ * @param openExternal Load url in browser instead of Cordova webview.
965
+ * @param clearHistory Clear the history stack, so new page becomes top of history
966
+ * @param params DroidGap parameters for new app
967
+ */
968
+ public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) { //throws android.content.ActivityNotFoundException {
969
+ LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);
970
+
971
+ // If clearing history
972
+ if (clearHistory) {
973
+ this.clearHistory();
974
+ }
975
+
976
+ // If loading into our webview
977
+ if (!openExternal) {
978
+
979
+ // Make sure url is in whitelist
980
+ if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) {
981
+ // TODO: What about params?
982
+
983
+ // Clear out current url from history, since it will be replacing it
984
+ if (clearHistory) {
985
+ this.urls.clear();
986
+ }
987
+
988
+ // Load new URL
989
+ this.loadUrl(url);
990
+ }
991
+ // Load in default viewer if not
992
+ else {
993
+ LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list. Loading into browser instead. (URL="+url+")");
994
+ try {
995
+ Intent intent = new Intent(Intent.ACTION_VIEW);
996
+ intent.setData(Uri.parse(url));
997
+ this.startActivity(intent);
998
+ } catch (android.content.ActivityNotFoundException e) {
999
+ LOG.e(TAG, "Error loading url "+url, e);
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ // Load in default view intent
1005
+ else {
1006
+ try {
1007
+ Intent intent = new Intent(Intent.ACTION_VIEW);
1008
+ intent.setData(Uri.parse(url));
1009
+ this.startActivity(intent);
1010
+ } catch (android.content.ActivityNotFoundException e) {
1011
+ LOG.e(TAG, "Error loading url "+url, e);
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ /**
1017
+ * Show the spinner. Must be called from the UI thread.
1018
+ *
1019
+ * @param title Title of the dialog
1020
+ * @param message The message of the dialog
1021
+ */
1022
+ public void spinnerStart(final String title, final String message) {
1023
+ if (this.spinnerDialog != null) {
1024
+ this.spinnerDialog.dismiss();
1025
+ this.spinnerDialog = null;
1026
+ }
1027
+ final DroidGap me = this;
1028
+ this.spinnerDialog = ProgressDialog.show(DroidGap.this, title , message, true, true,
1029
+ new DialogInterface.OnCancelListener() {
1030
+ public void onCancel(DialogInterface dialog) {
1031
+ me.spinnerDialog = null;
1032
+ }
1033
+ });
1034
+ }
1035
+
1036
+ /**
1037
+ * Stop spinner.
1038
+ */
1039
+ public void spinnerStop() {
1040
+ if (this.spinnerDialog != null) {
1041
+ this.spinnerDialog.dismiss();
1042
+ this.spinnerDialog = null;
1043
+ }
1044
+ }
1045
+
1046
+ /**
1047
+ * End this activity by calling finish for activity
1048
+ */
1049
+ public void endActivity() {
1050
+ this.activityState = ACTIVITY_EXITING;
1051
+ this.finish();
1052
+ }
1053
+
1054
+ /**
1055
+ * Called when a key is pressed.
1056
+ *
1057
+ * @param keyCode
1058
+ * @param event
1059
+ */
1060
+ @Override
1061
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
1062
+ if (this.appView == null) {
1063
+ return super.onKeyDown(keyCode, event);
1064
+ }
1065
+
1066
+ // If back key
1067
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
1068
+
1069
+ // If back key is bound, then send event to JavaScript
1070
+ if (this.bound) {
1071
+ this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('backbutton');");
1072
+ return true;
1073
+ }
1074
+
1075
+ // If not bound
1076
+ else {
1077
+
1078
+ // Go to previous page in webview if it is possible to go back
1079
+ if (this.backHistory()) {
1080
+ return true;
1081
+ }
1082
+
1083
+ // If not, then invoke behavior of super class
1084
+ else {
1085
+ this.activityState = ACTIVITY_EXITING;
1086
+ return super.onKeyDown(keyCode, event);
1087
+ }
1088
+ }
1089
+ }
1090
+
1091
+ // If menu key
1092
+ else if (keyCode == KeyEvent.KEYCODE_MENU) {
1093
+ this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('menubutton');");
1094
+ return super.onKeyDown(keyCode, event);
1095
+ }
1096
+
1097
+ // If search key
1098
+ else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
1099
+ this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('searchbutton');");
1100
+ return true;
1101
+ }
1102
+
1103
+ return false;
1104
+ }
1105
+
1106
+ /**
1107
+ * Any calls to Activity.startActivityForResult must use method below, so
1108
+ * the result can be routed to them correctly.
1109
+ *
1110
+ * This is done to eliminate the need to modify DroidGap.java to receive activity results.
1111
+ *
1112
+ * @param intent The intent to start
1113
+ * @param requestCode Identifies who to send the result to
1114
+ *
1115
+ * @throws RuntimeException
1116
+ */
1117
+ @Override
1118
+ public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
1119
+ LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode);
1120
+ super.startActivityForResult(intent, requestCode);
1121
+ }
1122
+
1123
+ /**
1124
+ * Launch an activity for which you would like a result when it finished. When this activity exits,
1125
+ * your onActivityResult() method will be called.
1126
+ *
1127
+ * @param command The command object
1128
+ * @param intent The intent to start
1129
+ * @param requestCode The request code that is passed to callback to identify the activity
1130
+ */
1131
+ public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
1132
+ this.activityResultCallback = command;
1133
+ this.activityResultKeepRunning = this.keepRunning;
1134
+
1135
+ // If multitasking turned on, then disable it for activities that return results
1136
+ if (command != null) {
1137
+ this.keepRunning = false;
1138
+ }
1139
+
1140
+ // Start activity
1141
+ super.startActivityForResult(intent, requestCode);
1142
+ }
1143
+
1144
+ @Override
1145
+ /**
1146
+ * Called when an activity you launched exits, giving you the requestCode you started it with,
1147
+ * the resultCode it returned, and any additional data from it.
1148
+ *
1149
+ * @param requestCode The request code originally supplied to startActivityForResult(),
1150
+ * allowing you to identify who this result came from.
1151
+ * @param resultCode The integer result code returned by the child activity through its setResult().
1152
+ * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
1153
+ */
1154
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
1155
+ super.onActivityResult(requestCode, resultCode, intent);
1156
+ IPlugin callback = this.activityResultCallback;
1157
+ if (callback != null) {
1158
+ callback.onActivityResult(requestCode, resultCode, intent);
1159
+ }
1160
+ }
1161
+
1162
+ public void setActivityResultCallback(IPlugin plugin) {
1163
+ this.activityResultCallback = plugin;
1164
+ }
1165
+
1166
+ /**
1167
+ * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
1168
+ * The errorCode parameter corresponds to one of the ERROR_* constants.
1169
+ *
1170
+ * @param errorCode The error code corresponding to an ERROR_* value.
1171
+ * @param description A String describing the error.
1172
+ * @param failingUrl The url that failed to load.
1173
+ */
1174
+ public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
1175
+ final DroidGap me = this;
1176
+
1177
+ // If errorUrl specified, then load it
1178
+ final String errorUrl = me.getStringProperty("errorUrl", null);
1179
+ if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
1180
+
1181
+ // Load URL on UI thread
1182
+ me.runOnUiThread(new Runnable() {
1183
+ public void run() {
1184
+ me.showWebPage(errorUrl, false, true, null);
1185
+ }
1186
+ });
1187
+ }
1188
+
1189
+ // If not, then display error dialog
1190
+ else {
1191
+ me.runOnUiThread(new Runnable() {
1192
+ public void run() {
1193
+ me.appView.setVisibility(View.GONE);
1194
+ me.displayError("Application Error", description + " ("+failingUrl+")", "OK", true);
1195
+ }
1196
+ });
1197
+ }
1198
+ }
1199
+
1200
+ /**
1201
+ * Display an error dialog and optionally exit application.
1202
+ *
1203
+ * @param title
1204
+ * @param message
1205
+ * @param button
1206
+ * @param exit
1207
+ */
1208
+ public void displayError(final String title, final String message, final String button, final boolean exit) {
1209
+ final DroidGap me = this;
1210
+ me.runOnUiThread(new Runnable() {
1211
+ public void run() {
1212
+ AlertDialog.Builder dlg = new AlertDialog.Builder(me);
1213
+ dlg.setMessage(message);
1214
+ dlg.setTitle(title);
1215
+ dlg.setCancelable(false);
1216
+ dlg.setPositiveButton(button,
1217
+ new AlertDialog.OnClickListener() {
1218
+ public void onClick(DialogInterface dialog, int which) {
1219
+ dialog.dismiss();
1220
+ if (exit) {
1221
+ me.endActivity();
1222
+ }
1223
+ }
1224
+ });
1225
+ dlg.create();
1226
+ dlg.show();
1227
+ }
1228
+ });
1229
+ }
1230
+
1231
+
1232
+ /**
1233
+ * Load Cordova configuration from res/xml/cordova.xml.
1234
+ * Approved list of URLs that can be loaded into DroidGap
1235
+ * <access origin="http://server regexp" subdomains="true" />
1236
+ * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
1237
+ * <log level="DEBUG" />
1238
+ */
1239
+ private void loadConfiguration() {
1240
+ int id = getResources().getIdentifier("cordova", "xml", getPackageName());
1241
+ if (id == 0) {
1242
+ LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
1243
+ return;
1244
+ }
1245
+ XmlResourceParser xml = getResources().getXml(id);
1246
+ int eventType = -1;
1247
+ while (eventType != XmlResourceParser.END_DOCUMENT) {
1248
+ if (eventType == XmlResourceParser.START_TAG) {
1249
+ String strNode = xml.getName();
1250
+ if (strNode.equals("access")) {
1251
+ String origin = xml.getAttributeValue(null, "origin");
1252
+ String subdomains = xml.getAttributeValue(null, "subdomains");
1253
+ if (origin != null) {
1254
+ this.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
1255
+ }
1256
+ }
1257
+ else if (strNode.equals("log")) {
1258
+ String level = xml.getAttributeValue(null, "level");
1259
+ LOG.i("CordovaLog", "Found log level %s", level);
1260
+ if (level != null) {
1261
+ LOG.setLogLevel(level);
1262
+ }
1263
+ }
1264
+ else if (strNode.equals("preference")) {
1265
+ String name = xml.getAttributeValue(null, "name");
1266
+ String value = xml.getAttributeValue(null, "value");
1267
+ String readonlyString = xml.getAttributeValue(null, "readonly");
1268
+
1269
+ boolean readonly = (readonlyString != null &&
1270
+ readonlyString.equals("true"));
1271
+
1272
+ LOG.i("CordovaLog", "Found preference for %s", name);
1273
+
1274
+ preferences.add(new PreferenceNode(name, value, readonly));
1275
+ }
1276
+ }
1277
+ try {
1278
+ eventType = xml.next();
1279
+ } catch (XmlPullParserException e) {
1280
+ e.printStackTrace();
1281
+ } catch (IOException e) {
1282
+ e.printStackTrace();
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ /**
1288
+ * Add entry to approved list of URLs (whitelist)
1289
+ *
1290
+ * @param origin URL regular expression to allow
1291
+ * @param subdomains T=include all subdomains under origin
1292
+ */
1293
+ private void addWhiteListEntry(String origin, boolean subdomains) {
1294
+ try {
1295
+ // Unlimited access to network resources
1296
+ if(origin.compareTo("*") == 0) {
1297
+ LOG.d(TAG, "Unlimited access to network resources");
1298
+ whiteList.add(Pattern.compile(".*"));
1299
+ } else { // specific access
1300
+ // check if subdomains should be included
1301
+ // TODO: we should not add more domains if * has already been added
1302
+ if (subdomains) {
1303
+ // XXX making it stupid friendly for people who forget to include protocol/SSL
1304
+ if(origin.startsWith("http")) {
1305
+ whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
1306
+ } else {
1307
+ whiteList.add(Pattern.compile("^https?://(.*\\.)?"+origin));
1308
+ }
1309
+ LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
1310
+ } else {
1311
+ // XXX making it stupid friendly for people who forget to include protocol/SSL
1312
+ if(origin.startsWith("http")) {
1313
+ whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
1314
+ } else {
1315
+ whiteList.add(Pattern.compile("^https?://"+origin));
1316
+ }
1317
+ LOG.d(TAG, "Origin to allow: %s", origin);
1318
+ }
1319
+ }
1320
+ } catch(Exception e) {
1321
+ LOG.d(TAG, "Failed to add origin %s", origin);
1322
+ }
1323
+ }
1324
+
1325
+ /**
1326
+ * Determine if URL is in approved list of URLs to load.
1327
+ *
1328
+ * @param url
1329
+ * @return
1330
+ */
1331
+ boolean isUrlWhiteListed(String url) {
1332
+
1333
+ // Check to see if we have matched url previously
1334
+ if (whiteListCache.get(url) != null) {
1335
+ return true;
1336
+ }
1337
+
1338
+ // Look for match in white list
1339
+ Iterator<Pattern> pit = whiteList.iterator();
1340
+ while (pit.hasNext()) {
1341
+ Pattern p = pit.next();
1342
+ Matcher m = p.matcher(url);
1343
+
1344
+ // If match found, then cache it to speed up subsequent comparisons
1345
+ if (m.find()) {
1346
+ whiteListCache.put(url, true);
1347
+ return true;
1348
+ }
1349
+ }
1350
+ return false;
1351
+ }
1352
+
1353
+ /*
1354
+ * URL stack manipulators
1355
+ */
1356
+
1357
+ /**
1358
+ * Returns the top url on the stack without removing it from
1359
+ * the stack.
1360
+ */
1361
+ public String peekAtUrlStack() {
1362
+ if (urls.size() > 0) {
1363
+ return urls.peek();
1364
+ }
1365
+ return "";
1366
+ }
1367
+
1368
+ /**
1369
+ * Add a url to the stack
1370
+ *
1371
+ * @param url
1372
+ */
1373
+ public void pushUrl(String url) {
1374
+ urls.push(url);
1375
+ }
1376
+
1377
+ /*
1378
+ * Hook in DroidGap for menu plugins
1379
+ *
1380
+ */
1381
+
1382
+ @Override
1383
+ public boolean onCreateOptionsMenu(Menu menu)
1384
+ {
1385
+ this.postMessage("onCreateOptionsMenu", menu);
1386
+ return super.onCreateOptionsMenu(menu);
1387
+ }
1388
+
1389
+ @Override
1390
+ public boolean onPrepareOptionsMenu(Menu menu)
1391
+ {
1392
+ this.postMessage("onPrepareOptionsMenu", menu);
1393
+ return super.onPrepareOptionsMenu(menu);
1394
+ }
1395
+
1396
+ @Override
1397
+ public boolean onOptionsItemSelected(MenuItem item)
1398
+ {
1399
+ this.postMessage("onOptionsItemSelected", item);
1400
+ return true;
1401
+ }
1402
+
1403
+ public Context getContext() {
1404
+ return this;
1405
+ }
1406
+
1407
+ public void bindBackButton(boolean override) {
1408
+ // TODO Auto-generated method stub
1409
+ this.bound = override;
1410
+ }
1411
+
1412
+ public boolean isBackButtonBound() {
1413
+ // TODO Auto-generated method stub
1414
+ return this.bound;
1415
+ }
1416
+
1417
+ }