mobile_template 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.
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
+ }