forked from github/cordova-android
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8057bca4f2 | |||
| 546b33c936 | |||
| 9b5a63e8e4 | |||
| 798d502083 | |||
| 813637eb78 | |||
| e4a5000f9e | |||
| fd07cfc461 | |||
| d1ec1f7693 | |||
| 2d690ad014 | |||
| ff2ee67cb8 | |||
| 505081f42f | |||
| 80ee464dce | |||
| e4a992095f | |||
| aa6e011255 | |||
| 12816aa8f3 | |||
| 60b5d42324 | |||
| d3d1c06c09 | |||
| 010e162cd7 | |||
| 191e1bf150 | |||
| cdeddf1bb2 | |||
| bd7ed19b52 | |||
| 64b770b761 | |||
| 628473cc5b | |||
| 75f3651376 | |||
| 3e25953d33 | |||
| 996791696b | |||
| a08854eaf0 | |||
| 81ec4bc4fb | |||
| 0b9ed0c7c7 | |||
| ffb614dd91 | |||
| e26685becf | |||
| 90b8c20e28 | |||
| a8db71cd67 | |||
| 543fe1f43f | |||
| 1f2f9a02dd | |||
| 551f0f7e4a | |||
| 369d140040 | |||
| ac509369e9 | |||
| 5faed8378c | |||
| 7ee04ebf31 | |||
| 0ec47c5bd8 | |||
| fe3e7041b7 | |||
| 75ab33ad0c | |||
| 1377f60b29 | |||
| 8b595f9796 | |||
| 1f8d6b4866 | |||
| ea87dfe08a | |||
| 2d388025ad | |||
| 424078432a | |||
| bb2bd22fad | |||
| 6b29787df7 | |||
| 6a9253e928 | |||
| 97faebda41 | |||
| f111ea56ed | |||
| e2acd1af33 | |||
| 19332c1903 | |||
| b4c4fa9667 | |||
| c067299ace | |||
| 648df2624f | |||
| 1e4dc1bf5d | |||
| 1761cbb3dc |
@@ -1,7 +1,7 @@
|
||||
PhoneGap/Android
|
||||
PhoneGap Android
|
||||
===
|
||||
|
||||
PhoneGap/Android is an Android application library that allows for PhoneGap based projects to be built for the Android Platform. PhoneGap based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
|
||||
PhoneGap Android is an Android application library that allows for PhoneGap based projects to be built for the Android Platform. PhoneGap based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
|
||||
|
||||
Requires
|
||||
---
|
||||
@@ -10,21 +10,21 @@ Requires
|
||||
- Apache ANT
|
||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||
|
||||
PhoneGap/Android Developer Tools
|
||||
PhoneGap Android Developer Tools
|
||||
---
|
||||
|
||||
The PhoneGap developer tooling is split between general tooling and project level tooling.
|
||||
|
||||
Commands
|
||||
General Commands
|
||||
|
||||
./bin/create [path package activity] ... create the ./exmaple app or a phonegap/android project
|
||||
./bin/create [path package activity] ... create the ./example app or a phonegap android project
|
||||
./bin/bench ............................ generate a bench proj
|
||||
./bin/autotest ......................... test the cli tools
|
||||
./bin/test ............................. run mobile-spec
|
||||
|
||||
Project Commands
|
||||
|
||||
These commands live in a generated PhoneGap/Android project.
|
||||
These commands live in a generated PhoneGap Android project.
|
||||
|
||||
./phonegap/debug [path] ..................... install to first device
|
||||
./phonegap/emulate .......................... start avd (emulator) named default
|
||||
@@ -37,7 +37,7 @@ Start avd (emulator) named `default`:
|
||||
|
||||
./bin/emulate
|
||||
|
||||
Create the exmaple project and build it to the first device:
|
||||
Create the example project and build it to the first device:
|
||||
|
||||
./bin/create
|
||||
cd example
|
||||
@@ -47,15 +47,17 @@ Start adb logcat (console.log calls output here):
|
||||
|
||||
./phonegap/log
|
||||
|
||||
Running the [phonegap/mobile-spec](http://github.com/phonegap/mobile-spec) tests:
|
||||
Running the [callback/callback-test](http://github.com/callback/callback-test) tests:
|
||||
---
|
||||
|
||||
./bin/test
|
||||
|
||||
Create a new PhoneGap/Android Project
|
||||
Creating a new PhoneGap Android Project
|
||||
---
|
||||
|
||||
./bin/create ~/Desktop/myapp com.phonegap.special MyApp
|
||||
|
||||
Importing a PhoneGap/Android Project into Eclipse
|
||||
Importing a PhoneGap Android Project into Eclipse
|
||||
----
|
||||
|
||||
1. File > New > Project...
|
||||
|
||||
@@ -17,7 +17,7 @@ MANIFEST_PATH=./AndroidManifest.xml
|
||||
android create project --target $TARGET --path . --package $PACKAGE --activity $ACTIVITY
|
||||
|
||||
# copy all the phonegap scripts etc in there
|
||||
cp -R ./phonegap/templates/project/ .
|
||||
cp -R ./phonegap/templates/project/* .
|
||||
|
||||
# copy in phonegap.js
|
||||
cp ./.phonegap/android/phonegap-$VERSION.js ./assets/www
|
||||
@@ -29,8 +29,8 @@ cp ./.phonegap/android/phonegap-$VERSION.jar ./libs
|
||||
cat ./phonegap/templates/Activity.java > $ACTIVITY_PATH
|
||||
|
||||
# interpolate the acivity name and package
|
||||
find "$ACTIVITY_PATH" | xargs grep '__ACTIVITY__' -sl | xargs -L1 sed -i "" "s/__ACTIVITY__/${ACTIVITY}/g"
|
||||
find "$ACTIVITY_PATH" | xargs grep '__ID__' -sl | xargs -L1 sed -i "" "s/__ID__/${PACKAGE}/g"
|
||||
find "$ACTIVITY_PATH" | xargs grep '__ACTIVITY__' -sl | xargs -L1 sed -i "s/__ACTIVITY__/${ACTIVITY}/g"
|
||||
find "$ACTIVITY_PATH" | xargs grep '__ID__' -sl | xargs -L1 sed -i "s/__ID__/${PACKAGE}/g"
|
||||
|
||||
find "$MANIFEST_PATH" | xargs grep '__ACTIVITY__' -sl | xargs -L1 sed -i "" "s/__ACTIVITY__/${ACTIVITY}/g"
|
||||
find "$MANIFEST_PATH" | xargs grep '__PACKAGE__' -sl | xargs -L1 sed -i "" "s/__PACKAGE__/${PACKAGE}/g"
|
||||
find "$MANIFEST_PATH" | xargs grep '__ACTIVITY__' -sl | xargs -L1 sed -i "s/__ACTIVITY__/${ACTIVITY}/g"
|
||||
find "$MANIFEST_PATH" | xargs grep '__PACKAGE__' -sl | xargs -L1 sed -i "s/__PACKAGE__/${PACKAGE}/g"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap-1.2.0.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phonegap>
|
||||
<access origin="http://127.0.0.1*"/>
|
||||
<log level="DEBUG"/>
|
||||
</phonegap>
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
VERSION=$(cat ./VERSION)
|
||||
|
||||
# get the latest mobile-spec
|
||||
git clone git@github.com:phonegap/mobile-spec.git
|
||||
git clone git@github.com:callback/callback-test.git
|
||||
|
||||
# clobber test if it exists
|
||||
if [ -e ./test ]
|
||||
@@ -17,7 +17,10 @@ fi
|
||||
|
||||
# kill the default app and replace it w/ mobile-spec
|
||||
rm -rf ./test/assets/www
|
||||
mv ./mobile-spec ./test/assets/www
|
||||
mv ./callback-test ./test/assets/www
|
||||
|
||||
# copy in phonegap.js since www dir was replaced above
|
||||
cp ./framework/assets/www/phonegap-$VERSION.js ./test/assets/www/phonegap-$VERSION.js
|
||||
|
||||
# build it, launch it and start logging on stdout
|
||||
cd ./test && ./bin/debug && ./bin/log
|
||||
cd ./test && ./phonegap/debug && ./phonegap/log
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/commons-codec-1.3.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
||||
package="com.phonegap" android:versionName="1.1" android:versionCode="5">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
<!-- android:xlargeScreens="true" screen supported only after Android-9 -->
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
@@ -31,7 +30,7 @@
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
android:debuggable="true">
|
||||
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
|
||||
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
|
||||
<intent-filter>
|
||||
@@ -46,5 +45,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# This file is used to override default values used by the Ant build system.
|
||||
#
|
||||
# This file must be checked in Version Control Systems, as it is
|
||||
# integral to the build system of your project.
|
||||
|
||||
# This file is only used by the Ant script.
|
||||
|
||||
# You can use this to override default values such as
|
||||
# 'source.dir' for the location of your java source folder and
|
||||
# 'out.dir' for the location of your output folder.
|
||||
|
||||
# You can also use it define how the release builds are signed by declaring
|
||||
# the following properties:
|
||||
# 'key.store' for the location of your keystore and
|
||||
# 'key.alias' for the name of the key to use.
|
||||
# The password will be asked during the build when you use the 'release' target.
|
||||
|
||||
@@ -24,21 +24,18 @@ App.prototype.clearCache = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
* Load the url into the webview or into new browser instance.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs.
|
||||
* loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html");
|
||||
* keepRunning: boolean => enable app to keep running in background
|
||||
* clearHistory: boolean => clear webview history (default=false)
|
||||
* openExternal: boolean => open in a new browser (default=false)
|
||||
*
|
||||
* Example:
|
||||
* App app = new App();
|
||||
* app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
*/
|
||||
App.prototype.loadUrl = function(url, props) {
|
||||
PhoneGap.exec(null, null, "App", "loadUrl", [url, props]);
|
||||
@@ -99,8 +96,6 @@ App.prototype.addWhiteListEntry = function(origin, subdomains) {
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.app = new App();
|
||||
navigator.app.origHistoryBack = window.history.back;
|
||||
window.history.back = navigator.app.backHistory;
|
||||
});
|
||||
}());
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ var MediaError = function() {
|
||||
this.message = "";
|
||||
};
|
||||
|
||||
MediaError.MEDIA_ERR_NONE_ACTIVE = 0;
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
MediaError.MEDIA_ERR_DECODE = 3;
|
||||
@@ -211,7 +212,7 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
}
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
media.errorCallback(value);
|
||||
media.errorCallback({"code":value});
|
||||
}
|
||||
}
|
||||
else if (msg == Media.MEDIA_POSITION) {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
// Version 1.2.0
|
||||
|
||||
if (typeof PhoneGap === "undefined") {
|
||||
|
||||
/**
|
||||
@@ -23,13 +25,18 @@ if (typeof PhoneGap === "undefined") {
|
||||
* onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
|
||||
*
|
||||
* The only PhoneGap events that user code should register for are:
|
||||
* onDeviceReady
|
||||
* onResume
|
||||
* deviceready PhoneGap native code is initialized and PhoneGap APIs can be called from JavaScript
|
||||
* pause App has moved to background
|
||||
* resume App has returned to foreground
|
||||
*
|
||||
* Listeners can be registered as:
|
||||
* document.addEventListener("deviceready", myDeviceReadyListener, false);
|
||||
* document.addEventListener("resume", myResumeListener, false);
|
||||
* document.addEventListener("pause", myPauseListener, false);
|
||||
*
|
||||
* The DOM lifecycle events should be used for saving and restoring state
|
||||
* window.onload
|
||||
* window.onunload
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) !== 'object') {
|
||||
@@ -371,9 +378,6 @@ PhoneGap.Channel.join(function() {
|
||||
prompt("", "gap_init:");
|
||||
|
||||
PhoneGap.onDeviceReady.fire();
|
||||
|
||||
// Fire the onresume event, since first one happens before JavaScript is loaded
|
||||
PhoneGap.onResume.fire();
|
||||
}, PhoneGap.deviceReadyChannelsArray);
|
||||
|
||||
}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);
|
||||
@@ -917,7 +921,7 @@ PhoneGap.JSCallback = function() {
|
||||
|
||||
// If server is stopping
|
||||
else if (xmlhttp.status === 503) {
|
||||
console.log("JSCallback Error: Service unavailable. Stopping callbacks.");
|
||||
console.log("JSCallback Server Closed: Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If request wasn't GET
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap-1.1.0.js"></script>
|
||||
<script src="phonegap-1.2.0.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
Executable → Regular
+65
-48
@@ -9,70 +9,87 @@
|
||||
</loadfile>
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||
Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<loadproperties srcFile="local.properties" />
|
||||
|
||||
<!-- The build.properties file can be created by you and is never touched
|
||||
by the 'android' tool. This is the place to change some of the default property values
|
||||
used by the Ant rules.
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
application.package
|
||||
the name of your application package as defined in the manifest. Used by the
|
||||
'uninstall' rule.
|
||||
source.dir
|
||||
the name of the source directory. Default is 'src'.
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
the name of the output directory. Default is 'bin'.
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
Properties related to the SDK location or the project target should be updated
|
||||
using the 'android' tool with the 'update' action.
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems.
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="build.properties" />
|
||||
|
||||
<!-- The default.properties file is created and updated by the 'android' tool, as well
|
||||
as ADT.
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems. -->
|
||||
<property file="default.properties" />
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- We need to setup the double quote. -->
|
||||
<property name="dblQuote">"</property>
|
||||
|
||||
<!-- Custom Android task to deal with the project target, and import the proper rules.
|
||||
This requires ant 1.6.0 or above. -->
|
||||
<path id="android.antlibs">
|
||||
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
|
||||
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
|
||||
</path>
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
<taskdef name="setup"
|
||||
classname="com.android.ant.SetupTask"
|
||||
classpathref="android.antlibs" />
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
<!-- Execute the Android Setup task that will setup some properties specific to the target,
|
||||
and import the build rules files.
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
The rules file is imported from
|
||||
<SDK>/platforms/<target_platform>/templates/android_rules.xml
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
To customize some build steps for your project:
|
||||
- copy the content of the main node <project> from android_rules.xml
|
||||
- paste it in this build.xml below the <setup /> task.
|
||||
- disable the import by changing the setup task below to <setup import="false" />
|
||||
<!-- version-tag: custom -->
|
||||
<!-- extension targets. Uncomment the ones where you want to do custom work
|
||||
in between standard targets -->
|
||||
<!--
|
||||
<target name="-pre-build">
|
||||
</target>
|
||||
<target name="-pre-compile">
|
||||
</target>
|
||||
|
||||
This will ensure that the properties are setup correctly but that your customized
|
||||
build steps are used.
|
||||
/* This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir} */
|
||||
<target name="-post-compile">
|
||||
</target>
|
||||
-->
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<setup />
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
|
||||
<target name="check-javascript" depends="build-javascript">
|
||||
<delete dir="assets/lib"/>
|
||||
@@ -122,7 +139,8 @@
|
||||
<fileset file="assets/www/index.html" />
|
||||
<!-- <fileset file="../bin/templates/project/assets/www/index.html" /> -->
|
||||
</replaceregexp>
|
||||
<replaceregexp match="phonegapVersion = [\u0022].*[\u0022];" replace="phonegapVersion = ${dblQuote}${version}${dblQuote};" byline="true">
|
||||
<!-- This is sketchy, but it works, ${dblQuote} does not -->
|
||||
<replaceregexp match="phonegapVersion = [\u0022].*[\u0022];" replace='phonegapVersion = ${dblQuote}${version}${dblQuote};' byline="true">
|
||||
<fileset file="src/com/phonegap/Device.java" />
|
||||
</replaceregexp>
|
||||
|
||||
@@ -133,7 +151,7 @@
|
||||
<!-- Build PhoneGap jar file that includes all native code, and PhoneGap JS file
|
||||
that includes all JavaScript code.
|
||||
-->
|
||||
<target name="jar" depends="build-javascript, compile">
|
||||
<target name="jar" depends="build-javascript, -compile">
|
||||
<jar jarfile="phonegap-${version}.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
</target>
|
||||
|
||||
@@ -143,5 +161,4 @@
|
||||
<target name="phonegap_release" depends="build-javascript, release">
|
||||
</target>
|
||||
|
||||
|
||||
</project>
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-12
|
||||
target=android-14
|
||||
apk-configurations=
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system use,
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-14
|
||||
apk-configurations=
|
||||
Executable → Regular
+1
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phonegap>
|
||||
<access origin="http://127.0.0.1*"/>
|
||||
<log level="DEBUG"/>
|
||||
</phonegap>
|
||||
|
||||
@@ -10,7 +10,7 @@ package com.phonegap;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.app.Activity;
|
||||
import com.phonegap.api.LOG;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import java.util.HashMap;
|
||||
@@ -78,18 +78,18 @@ public class App extends Plugin {
|
||||
((DroidGap)this.ctx).clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url
|
||||
* @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...)
|
||||
* @throws JSONException
|
||||
*/
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url
|
||||
* @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...)
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void loadUrl(String url, JSONObject props) throws JSONException {
|
||||
System.out.println("App.loadUrl("+url+","+props+")");
|
||||
LOG.d("App", "App.loadUrl("+url+","+props+")");
|
||||
int wait = 0;
|
||||
boolean usePhoneGap = true;
|
||||
boolean clearPrev = false;
|
||||
boolean openExternal = false;
|
||||
boolean clearHistory = false;
|
||||
|
||||
// If there are properties, then set them on the Activity
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
@@ -100,11 +100,11 @@ public class App extends Plugin {
|
||||
if (key.equals("wait")) {
|
||||
wait = props.getInt(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("usephonegap")) {
|
||||
usePhoneGap = props.getBoolean(key);
|
||||
else if (key.equalsIgnoreCase("openexternal")) {
|
||||
openExternal = props.getBoolean(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("clearprev")) {
|
||||
clearPrev = props.getBoolean(key);
|
||||
else if (key.equalsIgnoreCase("clearhistory")) {
|
||||
clearHistory = props.getBoolean(key);
|
||||
}
|
||||
else {
|
||||
Object value = props.get(key);
|
||||
@@ -135,7 +135,7 @@ public class App extends Plugin {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params);
|
||||
((DroidGap)this.ctx).showWebPage(url, openExternal, clearHistory, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,12 +146,10 @@ public class App extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear web history in this web view.
|
||||
* This does not have any effect since each page has its own activity.
|
||||
* Clear page history for the app.
|
||||
*/
|
||||
public void clearHistory() {
|
||||
((DroidGap)this.ctx).clearHistory();
|
||||
// TODO: Kill previous activities?
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +157,7 @@ public class App extends Plugin {
|
||||
* This is the same as pressing the backbutton on Android device.
|
||||
*/
|
||||
public void backHistory() {
|
||||
((DroidGap)this.ctx).endActivity();
|
||||
((DroidGap)this.ctx).backHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +167,7 @@ public class App extends Plugin {
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
public void overrideBackbutton(boolean override) {
|
||||
System.out.println("WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
|
||||
LOG.i("DroidGap", "WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
|
||||
((DroidGap)this.ctx).bound = override;
|
||||
}
|
||||
|
||||
@@ -186,7 +184,6 @@ public class App extends Plugin {
|
||||
* Exit the Android application.
|
||||
*/
|
||||
public void exitApp() {
|
||||
((DroidGap)this.ctx).setResult(Activity.RESULT_OK);
|
||||
((DroidGap)this.ctx).endActivity();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,16 +45,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
private static int MEDIA_POSITION = 3;
|
||||
private static int MEDIA_ERROR = 9;
|
||||
|
||||
// AudioPlayer error codes
|
||||
private static int MEDIA_ERROR_PLAY_MODE_SET = 1;
|
||||
private static int MEDIA_ERROR_ALREADY_RECORDING = 2;
|
||||
private static int MEDIA_ERROR_STARTING_RECORDING = 3;
|
||||
private static int MEDIA_ERROR_RECORD_MODE_SET = 4;
|
||||
private static int MEDIA_ERROR_STARTING_PLAYBACK = 5;
|
||||
private static int MEDIA_ERROR_RESUME_STATE = 6;
|
||||
private static int MEDIA_ERROR_PAUSE_STATE = 7;
|
||||
private static int MEDIA_ERROR_STOP_STATE = 8;
|
||||
|
||||
// Media error codes
|
||||
private static int MEDIA_ERR_NONE_ACTIVE = 0;
|
||||
private static int MEDIA_ERR_ABORTED = 1;
|
||||
private static int MEDIA_ERR_NETWORK = 2;
|
||||
private static int MEDIA_ERR_DECODE = 3;
|
||||
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
|
||||
|
||||
private AudioHandler handler; // The AudioHandler object
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private int state = MEDIA_NONE; // State of recording or playback
|
||||
@@ -108,7 +105,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void startRecording(String file) {
|
||||
if (this.mPlayer != null) {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PLAY_MODE_SET+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
|
||||
// Make sure we're not already recording
|
||||
@@ -129,11 +126,11 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_RECORDING+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_ALREADY_RECORDING+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +172,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void startPlaying(String file) {
|
||||
if (this.recorder != null) {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RECORD_MODE_SET+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
|
||||
// If this is a new request to play audio, or stopped
|
||||
@@ -220,7 +217,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_PLAYBACK+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +231,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RESUME_STATE+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_ABORTED+");");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,7 +259,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PAUSE_STATE+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_NONE_ACTIVE+");");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +273,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STOP_STATE+");");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERR_NONE_ACTIVE+");");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ public class CallbackServer implements Runnable {
|
||||
*/
|
||||
public void init(String url) {
|
||||
//System.out.println("CallbackServer.start("+url+")");
|
||||
this.active = false;
|
||||
this.empty = true;
|
||||
this.port = 0;
|
||||
this.javascript = new LinkedList<String>();
|
||||
|
||||
// Determine if XHR or polling is to be used
|
||||
if ((url != null) && !url.startsWith("file://")) {
|
||||
@@ -119,6 +123,16 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-init when loading a new HTML page into webview.
|
||||
*
|
||||
* @param url The URL of the PhoneGap app being loaded
|
||||
*/
|
||||
public void reinit(String url) {
|
||||
this.stopServer();
|
||||
this.init(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if polling is being used instead of XHR.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.json.JSONObject;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import com.phonegap.api.LOG;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
@@ -30,7 +31,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
@@ -304,11 +304,11 @@ public class CameraLauncher extends Plugin {
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("Can't write to internal media storage.");
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
this.failPicture("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.phonegap.api.LOG;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
@@ -243,11 +244,11 @@ public class Capture extends Plugin {
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("Can't write to internal media storage.");
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
this.fail("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Regular → Executable
+1
-1
@@ -18,7 +18,7 @@ import android.provider.Settings;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "1.1.0"; // PhoneGap version
|
||||
public static String phonegapVersion = "1.2.0"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.phonegap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.Iterator;
|
||||
@@ -29,6 +30,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioManager;
|
||||
@@ -42,6 +44,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
@@ -55,6 +58,7 @@ import android.webkit.WebViewClient;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.phonegap.api.LOG;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.IPlugin;
|
||||
import com.phonegap.api.PluginManager;
|
||||
@@ -103,10 +107,6 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* // (String - default=null)
|
||||
* super.setStringProperty("loadingPageDialog", "Loading page...");
|
||||
*
|
||||
* // Cause all links on web page to be loaded into existing web view,
|
||||
* // instead of being loaded into new browser. (Boolean - default=false)
|
||||
* super.setBooleanProperty("loadInWebView", true);
|
||||
*
|
||||
* // Load a splash screen image from the resource drawable directory.
|
||||
* // (Integer - default=0)
|
||||
* super.setIntegerProperty("splashscreen", R.drawable.splash);
|
||||
@@ -125,9 +125,29 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
*
|
||||
* // Enable app to keep running in background. (Boolean - default=true)
|
||||
* super.setBooleanProperty("keepRunning", false);
|
||||
*
|
||||
* Phonegap.xml configuration:
|
||||
* PhoneGap uses a configuration file at res/xml/phonegap.xml to specify the following settings.
|
||||
*
|
||||
* Approved list of URLs that can be loaded into DroidGap
|
||||
* <access origin="http://server regexp" subdomains="true" />
|
||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
||||
* <log level="DEBUG" />
|
||||
*
|
||||
* Phonegap plugins:
|
||||
* PhoneGap uses a file at res/xml/plugins.xml to list all plugins that are installed.
|
||||
* Before using a new plugin, a new element must be added to the file.
|
||||
* name attribute is the service name passed to PhoneGap.exec() in JavaScript
|
||||
* value attribute is the Java class name to call.
|
||||
*
|
||||
* <plugins>
|
||||
* <plugin name="App" value="com.phonegap.App"/>
|
||||
* ...
|
||||
* </plugins>
|
||||
*/
|
||||
public class DroidGap extends PhonegapActivity {
|
||||
|
||||
public static String TAG = "DroidGap";
|
||||
|
||||
// The webview for our app
|
||||
protected WebView appView;
|
||||
protected WebViewClient webViewClient;
|
||||
@@ -139,13 +159,20 @@ public class DroidGap extends PhonegapActivity {
|
||||
public CallbackServer callbackServer;
|
||||
protected PluginManager pluginManager;
|
||||
protected boolean cancelLoadUrl = false;
|
||||
protected boolean clearHistory = false;
|
||||
protected ProgressDialog spinnerDialog = null;
|
||||
|
||||
// The initial URL for our app
|
||||
// ie http://server/path/index.html#abc?query
|
||||
private String url;
|
||||
private boolean firstPage = true;
|
||||
private String url = null;
|
||||
private Stack<String> urls = new Stack<String>();
|
||||
|
||||
// Url was specified from extras (activity was started programmatically)
|
||||
private String initUrl = null;
|
||||
|
||||
private static int ACTIVITY_STARTING = 0;
|
||||
private static int ACTIVITY_RUNNING = 1;
|
||||
private static int ACTIVITY_EXITING = 2;
|
||||
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
|
||||
|
||||
// The base of the initial URL for our app.
|
||||
// Does not include file name. Ends with /
|
||||
@@ -155,7 +182,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Plugin to call when activity result is received
|
||||
protected IPlugin activityResultCallback = null;
|
||||
protected boolean activityResultKeepRunning;
|
||||
private static int PG_REQUEST_CODE = 99;
|
||||
|
||||
// Flag indicates that a loadUrl timeout occurred
|
||||
private int loadUrlTimeout = 0;
|
||||
@@ -168,10 +194,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
* The variables below are used to cache some of the activity properties.
|
||||
*/
|
||||
|
||||
// Flag indicates that a URL navigated to from PhoneGap app should be loaded into same webview
|
||||
// instead of being loaded into the web browser.
|
||||
protected boolean loadInWebView = false;
|
||||
|
||||
// Draw a splash screen using an image located in the drawable resource directory.
|
||||
// This is not the same as calling super.loadSplashscreen(url)
|
||||
protected int splashscreen = 0;
|
||||
@@ -191,6 +213,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
LOG.d(TAG, "DroidGap.onCreate()");
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
@@ -207,17 +230,17 @@ public class DroidGap extends PhonegapActivity {
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
|
||||
// Load white list of allowed URLs
|
||||
this.loadWhiteList();
|
||||
// Load PhoneGap configuration:
|
||||
// white list of allowed URLs
|
||||
// debug setting
|
||||
this.loadConfiguration();
|
||||
|
||||
// If url was passed in to intent, then init webview, which will load the url
|
||||
this.firstPage = true;
|
||||
Bundle bundle = this.getIntent().getExtras();
|
||||
if (bundle != null) {
|
||||
String url = bundle.getString("url");
|
||||
if (url != null) {
|
||||
this.url = url;
|
||||
this.firstPage = false;
|
||||
this.initUrl = url;
|
||||
}
|
||||
}
|
||||
// Setup the hardware volume controls to handle volume control
|
||||
@@ -228,6 +251,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
* Create and initialize web container.
|
||||
*/
|
||||
public void init() {
|
||||
LOG.d(TAG, "DroidGap.init()");
|
||||
|
||||
// Create web container
|
||||
this.appView = new WebView(DroidGap.this);
|
||||
@@ -252,6 +276,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
//Set the nav dump for HTC
|
||||
settings.setNavDump(true);
|
||||
|
||||
// Enable database
|
||||
settings.setDatabaseEnabled(true);
|
||||
@@ -264,10 +291,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Enable built-in geolocation
|
||||
WebViewReflect.setGeolocationEnabled(settings, true);
|
||||
|
||||
// Create callback server and plugin manager
|
||||
this.callbackServer = new CallbackServer();
|
||||
this.pluginManager = new PluginManager(this.appView, this);
|
||||
|
||||
// Add web view but make it invisible while loading URL
|
||||
this.appView.setVisibility(View.INVISIBLE);
|
||||
root.addView(this.appView);
|
||||
@@ -295,24 +318,16 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
private void handleActivityParameters() {
|
||||
|
||||
// Init web view if not already done
|
||||
if (this.appView == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
// If backgroundColor
|
||||
this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
|
||||
this.root.setBackgroundColor(this.backgroundColor);
|
||||
|
||||
// If spashscreen
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
if (this.firstPage && (this.splashscreen != 0)) {
|
||||
if ((this.urls.size() == 0) && (this.splashscreen != 0)) {
|
||||
root.setBackgroundResource(this.splashscreen);
|
||||
}
|
||||
|
||||
// If loadInWebView
|
||||
this.loadInWebView = this.getBooleanProperty("loadInWebView", false);
|
||||
|
||||
// If loadUrlTimeoutValue
|
||||
int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0);
|
||||
if (timeout > 0) {
|
||||
@@ -331,12 +346,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void loadUrl(String url) {
|
||||
|
||||
// If first page of app, then set URL to load to be the one passed in
|
||||
if (this.firstPage) {
|
||||
if (this.initUrl == null || (this.urls.size() > 0)) {
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
// Otherwise use the URL specified in the activity's extras bundle
|
||||
else {
|
||||
this.loadUrlIntoView(this.url);
|
||||
this.loadUrlIntoView(this.initUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +361,15 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param url
|
||||
*/
|
||||
private void loadUrlIntoView(final String url) {
|
||||
System.out.println("loadUrl("+url+")");
|
||||
if (!url.startsWith("javascript:")) {
|
||||
LOG.d(TAG, "DroidGap.loadUrl(%s)", url);
|
||||
}
|
||||
|
||||
// Init web view if not already done
|
||||
if (this.appView == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.url = url;
|
||||
if (this.baseUrl == null) {
|
||||
int i = url.lastIndexOf('/');
|
||||
@@ -357,8 +380,10 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.baseUrl = this.url + "/";
|
||||
}
|
||||
}
|
||||
System.out.println("url="+url+" baseUrl="+baseUrl);
|
||||
|
||||
if (!url.startsWith("javascript:")) {
|
||||
LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl);
|
||||
}
|
||||
|
||||
// Load URL on UI thread
|
||||
final DroidGap me = this;
|
||||
this.runOnUiThread(new Runnable() {
|
||||
@@ -367,12 +392,28 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Handle activity parameters
|
||||
me.handleActivityParameters();
|
||||
|
||||
// Initialize callback server
|
||||
me.callbackServer.init(url);
|
||||
|
||||
// Track URLs loaded instead of using appView history
|
||||
me.urls.push(url);
|
||||
me.appView.clearHistory();
|
||||
|
||||
// Create callback server and plugin manager
|
||||
if (me.callbackServer == null) {
|
||||
me.callbackServer = new CallbackServer();
|
||||
me.callbackServer.init(url);
|
||||
}
|
||||
else {
|
||||
me.callbackServer.reinit(url);
|
||||
}
|
||||
if (me.pluginManager == null) {
|
||||
me.pluginManager = new PluginManager(me.appView, me);
|
||||
}
|
||||
else {
|
||||
me.pluginManager.reinit();
|
||||
}
|
||||
|
||||
// If loadingDialog property, then show the App loading dialog for first page of app
|
||||
String loading = null;
|
||||
if (me.firstPage) {
|
||||
if (me.urls.size() == 0) {
|
||||
loading = me.getStringProperty("loadingDialog", null);
|
||||
}
|
||||
else {
|
||||
@@ -412,6 +453,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If timeout, then stop loading and handle error
|
||||
if (me.loadUrlTimeout == currentLoadUrlTimeout) {
|
||||
me.appView.stopLoading();
|
||||
LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient");
|
||||
me.webViewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url);
|
||||
}
|
||||
}
|
||||
@@ -433,12 +475,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void loadUrl(final String url, int time) {
|
||||
|
||||
// If first page of app, then set URL to load to be the one passed in
|
||||
if (this.firstPage) {
|
||||
if (this.initUrl == null || (this.urls.size() > 0)) {
|
||||
this.loadUrlIntoView(url, time);
|
||||
}
|
||||
// Otherwise use the URL specified in the activity's extras bundle
|
||||
else {
|
||||
this.loadUrlIntoView(this.url);
|
||||
this.loadUrlIntoView(this.initUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,13 +492,18 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param time The number of ms to wait before loading webview
|
||||
*/
|
||||
private void loadUrlIntoView(final String url, final int time) {
|
||||
|
||||
// Clear cancel flag
|
||||
this.cancelLoadUrl = false;
|
||||
|
||||
// If not first page of app, then load immediately
|
||||
if (!this.firstPage) {
|
||||
this.loadUrl(url);
|
||||
if (this.urls.size() > 0) {
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
|
||||
System.out.println("loadUrl("+url+","+time+")");
|
||||
if (!url.startsWith("javascript:")) {
|
||||
LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
|
||||
}
|
||||
final DroidGap me = this;
|
||||
|
||||
// Handle activity parameters
|
||||
@@ -476,11 +523,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!me.cancelLoadUrl) {
|
||||
me.loadUrl(url);
|
||||
me.loadUrlIntoView(url);
|
||||
}
|
||||
else{
|
||||
me.cancelLoadUrl = false;
|
||||
System.out.println("Aborting loadUrl("+url+"): Another URL was loaded before timer expired.");
|
||||
LOG.d(TAG, "Aborting loadUrl(%s): Another URL was loaded before timer expired.", url);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -509,9 +556,22 @@ public class DroidGap extends PhonegapActivity {
|
||||
* Clear web history in this web view.
|
||||
*/
|
||||
public void clearHistory() {
|
||||
this.clearHistory = true;
|
||||
if (this.appView != null) {
|
||||
this.appView.clearHistory();
|
||||
this.urls.clear();
|
||||
|
||||
// Leave current url on history stack
|
||||
if (this.url != null) {
|
||||
this.urls.push(this.url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to previous page in history. (We manage our own history)
|
||||
*/
|
||||
public void backHistory() {
|
||||
if (this.urls.size() > 1) {
|
||||
this.urls.pop(); // Pop current url
|
||||
String url = this.urls.pop(); // Pop prev url that we want to load
|
||||
this.loadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,6 +708,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
// Don't process pause if shutting down, since onDestroy() will be called
|
||||
if (this.activityState == ACTIVITY_EXITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
@@ -683,6 +749,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (this.activityState == ACTIVITY_STARTING) {
|
||||
this.activityState = ACTIVITY_RUNNING;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
@@ -716,8 +788,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
if (this.appView != null) {
|
||||
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Send destroy event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};");
|
||||
@@ -729,7 +799,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.pluginManager.onDestroy();
|
||||
}
|
||||
else {
|
||||
this.finish();
|
||||
this.endActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,62 +824,60 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
* Load the specified URL in the PhoneGap webview or a new browser instance.
|
||||
*
|
||||
* NOTE: If usePhoneGap is set, only trusted PhoneGap URLs should be loaded,
|
||||
* since any PhoneGap API can be called by the loaded HTML page.
|
||||
* NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param usePhoneGap Load url in PhoneGap webview.
|
||||
* @param clearPrev Clear the activity stack, so new app becomes top of stack
|
||||
* @param openExternal Load url in browser instead of PhoneGap webview.
|
||||
* @param clearHistory Clear the history stack, so new page becomes top of history
|
||||
* @param params DroidGap parameters for new app
|
||||
* @throws android.content.ActivityNotFoundException
|
||||
*/
|
||||
public void showWebPage(String url, boolean usePhoneGap, boolean clearPrev, HashMap<String, Object> params) throws android.content.ActivityNotFoundException {
|
||||
Intent intent = null;
|
||||
if (usePhoneGap) {
|
||||
try {
|
||||
intent = new Intent().setClass(this, Class.forName(this.getComponentName().getClassName()));
|
||||
intent.putExtra("url", url);
|
||||
|
||||
// Add parameters
|
||||
if (params != null) {
|
||||
java.util.Set<Entry<String,Object>> s = params.entrySet();
|
||||
java.util.Iterator<Entry<String,Object>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Object> entry = it.next();
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
intent.putExtra(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
intent.putExtra(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
intent.putExtra(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.startActivityForResult(intent, PG_REQUEST_CODE);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.startActivity(intent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.startActivity(intent);
|
||||
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) { //throws android.content.ActivityNotFoundException {
|
||||
LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);
|
||||
|
||||
// If clearing history
|
||||
if (clearHistory) {
|
||||
this.clearHistory();
|
||||
}
|
||||
|
||||
// Finish current activity
|
||||
if (clearPrev) {
|
||||
this.endActivity();
|
||||
// If loading into our webview
|
||||
if (!openExternal) {
|
||||
|
||||
// Make sure url is in whitelist
|
||||
if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) {
|
||||
// TODO: What about params?
|
||||
|
||||
// Clear out current url from history, since it will be replacing it
|
||||
if (clearHistory) {
|
||||
this.urls.clear();
|
||||
}
|
||||
|
||||
// Load new URL
|
||||
this.loadUrl(url);
|
||||
}
|
||||
// Load in default viewer if not
|
||||
else {
|
||||
LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list. Loading into browser instead. (URL="+url+")");
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url "+url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load in default view intent
|
||||
else {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url "+url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,13 +942,32 @@ public class DroidGap extends PhonegapActivity {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Alert");
|
||||
dlg.setCancelable(false);
|
||||
//Don't let alerts break the back button
|
||||
dlg.setCancelable(true);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.confirm();
|
||||
}
|
||||
});
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.confirm();
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
//DO NOTHING
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if(keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
result.confirm();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
return true;
|
||||
@@ -899,7 +986,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Confirm");
|
||||
dlg.setCancelable(false);
|
||||
dlg.setCancelable(true);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@@ -912,6 +999,24 @@ public class DroidGap extends PhonegapActivity {
|
||||
result.cancel();
|
||||
}
|
||||
});
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.cancel();
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
//DO NOTHING
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if(keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
result.cancel();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
return true;
|
||||
@@ -922,6 +1027,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
*
|
||||
* Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
@@ -934,7 +1042,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Security check to make sure any requests are coming from the page initially
|
||||
// loaded in webview and not another loaded in an iframe.
|
||||
boolean reqOk = false;
|
||||
if (url.indexOf(this.ctx.baseUrl) == 0 || isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0 || isUrlWhiteListed(url)) {
|
||||
reqOk = true;
|
||||
}
|
||||
|
||||
@@ -1031,13 +1139,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
||||
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
||||
{
|
||||
Log.d(TAG, "event raised onExceededDatabaseQuota estimatedSize: " + Long.toString(estimatedSize) + " currentQuota: " + Long.toString(currentQuota) + " totalUsedQuota: " + Long.toString(totalUsedQuota));
|
||||
LOG.d(TAG, "DroidGap: onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
|
||||
|
||||
if( estimatedSize < MAX_QUOTA)
|
||||
{
|
||||
//increase for 1Mb
|
||||
long newQuota = estimatedSize;
|
||||
Log.d(TAG, "calling quotaUpdater.updateQuota newQuota: " + Long.toString(newQuota) );
|
||||
LOG.d(TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
|
||||
quotaUpdater.updateQuota(newQuota);
|
||||
}
|
||||
else
|
||||
@@ -1052,8 +1160,15 @@ public class DroidGap extends PhonegapActivity {
|
||||
@Override
|
||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||
{
|
||||
// This is a kludgy hack!!!!
|
||||
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
|
||||
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||
{
|
||||
LOG.d(TAG, consoleMessage.message());
|
||||
return super.onConsoleMessage(consoleMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1108,7 +1223,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error dialing "+url+": "+ e.toString());
|
||||
LOG.e(TAG, "Error dialing "+url+": "+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1119,7 +1234,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error showing map "+url+": "+ e.toString());
|
||||
LOG.e(TAG, "Error showing map "+url+": "+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,7 +1245,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending email "+url+": "+ e.toString());
|
||||
LOG.e(TAG, "Error sending email "+url+": "+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1162,7 +1277,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending sms "+url+":"+ e.toString());
|
||||
LOG.e(TAG, "Error sending sms "+url+":"+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1171,14 +1286,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
// If our app or file:, then load into a new phonegap webview container by starting a new instance of our activity.
|
||||
// Our app continues to run. When BACK is pressed, our app is redisplayed.
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0 || isUrlWhiteListed(url)) {
|
||||
try {
|
||||
// Init parameters to new DroidGap activity and propagate existing parameters
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
this.ctx.showWebPage(url, true, false, params);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error loading url into DroidGap - "+url+":"+ e.toString());
|
||||
}
|
||||
if (url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0 || isUrlWhiteListed(url)) {
|
||||
this.ctx.loadUrl(url);
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
@@ -1188,13 +1297,21 @@ public class DroidGap extends PhonegapActivity {
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error loading url "+url+":"+ e.toString());
|
||||
LOG.e(TAG, "Error loading url "+url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
|
||||
// Clear history so history.back() doesn't do anything.
|
||||
// So we can reinit() native side CallbackServer & PluginManager.
|
||||
view.clearHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has finished loading.
|
||||
*
|
||||
@@ -1216,35 +1333,31 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
// Make app visible after 2 sec in case there was a JS error and PhoneGap JS never initialized correctly
|
||||
Thread t = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
ctx.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
ctx.spinnerStop();
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
if (appView.getVisibility() == View.INVISIBLE) {
|
||||
Thread t = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
ctx.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
ctx.spinnerStop();
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
|
||||
|
||||
// Clear history, so that previous screen isn't there when Back button is pressed
|
||||
if (this.ctx.clearHistory) {
|
||||
this.ctx.clearHistory = false;
|
||||
this.ctx.appView.clearHistory();
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
||||
// Shutdown if blank loaded
|
||||
if (url.equals("about:blank")) {
|
||||
if (this.ctx.callbackServer != null) {
|
||||
this.ctx.callbackServer.destroy();
|
||||
}
|
||||
this.ctx.finish();
|
||||
this.ctx.endActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1259,7 +1372,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
System.out.println("onReceivedError: Error code="+errorCode+" Description="+description+" URL="+failingUrl);
|
||||
LOG.d(TAG, "DroidGap: GapViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl);
|
||||
|
||||
// Clear timeout flag
|
||||
this.ctx.loadUrlTimeout++;
|
||||
@@ -1294,10 +1407,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* End this activity by simulating backbutton keypress
|
||||
* End this activity by calling finish for activity
|
||||
*/
|
||||
public void endActivity() {
|
||||
super.onKeyDown(KeyEvent.KEYCODE_BACK, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
|
||||
this.activityState = ACTIVITY_EXITING;
|
||||
this.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1325,13 +1439,14 @@ public class DroidGap extends PhonegapActivity {
|
||||
else {
|
||||
|
||||
// Go to previous page in webview if it is possible to go back
|
||||
if (this.appView.canGoBack()) {
|
||||
this.appView.goBack();
|
||||
if (this.urls.size() > 1) {
|
||||
this.backHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not, then invoke behavior of super class
|
||||
else {
|
||||
this.activityState = ACTIVITY_EXITING;
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -1365,7 +1480,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
|
||||
System.out.println("startActivityForResult(intent,"+requestCode+")");
|
||||
LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode);
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
@@ -1402,17 +1517,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
// If a subsequent DroidGap activity is returning
|
||||
if (requestCode == PG_REQUEST_CODE) {
|
||||
// If terminating app, then shut down this activity too
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
this.setResult(Activity.RESULT_OK);
|
||||
this.endActivity();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
IPlugin callback = this.activityResultCallback;
|
||||
if (callback != null) {
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
@@ -1432,25 +1536,29 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param description A String describing the error.
|
||||
* @param failingUrl The url that failed to load.
|
||||
*/
|
||||
public void onReceivedError(int errorCode, String description, String failingUrl) {
|
||||
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
|
||||
final DroidGap me = this;
|
||||
|
||||
// If errorUrl specified, then load it
|
||||
final String errorUrl = me.getStringProperty("errorUrl", null);
|
||||
if ((errorUrl != null) && errorUrl.startsWith("file://") && (!failingUrl.equals(errorUrl))) {
|
||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||
|
||||
// Load URL on UI thread
|
||||
me.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
me.appView.loadUrl(errorUrl);
|
||||
me.showWebPage(errorUrl, true, true, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If not, then display error dialog
|
||||
else {
|
||||
me.appView.loadUrl("about:blank");
|
||||
me.displayError("Application Error", description + " ("+failingUrl+")", "OK", true);
|
||||
me.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
me.appView.setVisibility(View.GONE);
|
||||
me.displayError("Application Error", description + " ("+failingUrl+")", "OK", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1475,7 +1583,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
if (exit) {
|
||||
me.finish();
|
||||
me.endActivity();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1491,7 +1599,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
class LinearLayoutSoftKeyboardDetect extends LinearLayout {
|
||||
|
||||
private static final String LOG_TAG = "SoftKeyboardDetect";
|
||||
private static final String TAG = "SoftKeyboardDetect";
|
||||
|
||||
private int oldHeight = 0; // Need to save the old height as not to send redundant events
|
||||
private int oldWidth = 0; // Need to save old width for orientation change
|
||||
@@ -1520,23 +1628,22 @@ public class DroidGap extends PhonegapActivity {
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "We are in our onMeasure method");
|
||||
LOG.v(TAG, "We are in our onMeasure method");
|
||||
|
||||
// Get the current height of the visible part of the screen.
|
||||
// This height will not included the status bar.
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "Old Height = " + oldHeight);
|
||||
Log.d(LOG_TAG, "Height = " + height);
|
||||
Log.d(LOG_TAG, "Old Width = " + oldWidth);
|
||||
Log.d(LOG_TAG, "Width = " + width);
|
||||
|
||||
|
||||
|
||||
LOG.v(TAG, "Old Height = %d", oldHeight);
|
||||
LOG.v(TAG, "Height = %d", height);
|
||||
LOG.v(TAG, "Old Width = %d", oldWidth);
|
||||
LOG.v(TAG, "Width = %d", width);
|
||||
|
||||
// If the oldHeight = 0 then this is the first measure event as the app starts up.
|
||||
// If oldHeight == height then we got a measurement change that doesn't affect us.
|
||||
if (oldHeight == 0 || oldHeight == height) {
|
||||
Log.d(LOG_TAG, "Ignore this event");
|
||||
LOG.d(TAG, "Ignore this event");
|
||||
}
|
||||
// Account for orientation change and ignore this event/Fire orientation change
|
||||
else if(screenHeight == width)
|
||||
@@ -1544,18 +1651,18 @@ public class DroidGap extends PhonegapActivity {
|
||||
int tmp_var = screenHeight;
|
||||
screenHeight = screenWidth;
|
||||
screenWidth = tmp_var;
|
||||
Log.d(LOG_TAG, "Orientation Change");
|
||||
LOG.v(TAG, "Orientation Change");
|
||||
}
|
||||
// If the height as gotten bigger then we will assume the soft keyboard has
|
||||
// gone away.
|
||||
else if (height > oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw hide keyboard event");
|
||||
LOG.v(TAG, "Throw hide keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('hidekeyboard');");
|
||||
}
|
||||
// If the height as gotten smaller then we will assume the soft keyboard has
|
||||
// been displayed.
|
||||
else if (height < oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw show keyboard event");
|
||||
LOG.v(TAG, "Throw show keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('showkeyboard');");
|
||||
}
|
||||
|
||||
@@ -1566,14 +1673,16 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load approved list of URLs that can be loaded into DroidGap.
|
||||
* This list is loaded from res/xml/phonegap.xml
|
||||
* Load PhoneGap configuration from res/xml/phonegap.xml.
|
||||
* Approved list of URLs that can be loaded into DroidGap
|
||||
* <access origin="http://server regexp" subdomains="true" />
|
||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadWhiteList() {
|
||||
private void loadConfiguration() {
|
||||
int id = getResources().getIdentifier("phonegap", "xml", getPackageName());
|
||||
if (id == 0) {
|
||||
Log.i("PhoneGapLog", "phonegap.xml missing. Ignoring...");
|
||||
LOG.i("PhoneGapLog", "phonegap.xml missing. Ignoring...");
|
||||
return;
|
||||
}
|
||||
XmlResourceParser xml = getResources().getXml(id);
|
||||
@@ -1588,6 +1697,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||
}
|
||||
}
|
||||
else if (strNode.equals("log")) {
|
||||
String level = xml.getAttributeValue(null, "level");
|
||||
LOG.i("PhoneGapLog", "Found log level %s", level);
|
||||
if (level != null) {
|
||||
LOG.setLogLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
eventType = xml.next();
|
||||
@@ -1606,13 +1722,35 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param subdomains T=include all subdomains under origin
|
||||
*/
|
||||
public void addWhiteListEntry(String origin, boolean subdomains) {
|
||||
if (subdomains) {
|
||||
Log.d("PhoneGapLog", "Origin to allow with subdomains: "+origin);
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https{0,1}://", "^https{0,1}://.*")));
|
||||
} else {
|
||||
Log.d("PhoneGapLog", "Origin to allow: "+origin);
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https{0,1}://", "^https{0,1}://")));
|
||||
}
|
||||
try {
|
||||
// Unlimited access to network resources
|
||||
if(origin.compareTo("*") == 0) {
|
||||
LOG.d(TAG, "Unlimited access to network resources");
|
||||
whiteList.add(Pattern.compile("*"));
|
||||
} else { // specific access
|
||||
// check if subdomains should be included
|
||||
// TODO: we should not add more domains if * has already been added
|
||||
if (subdomains) {
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
if(origin.startsWith("http")) {
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https{0,1}://", "^https{0,1}://.*")));
|
||||
} else {
|
||||
whiteList.add(Pattern.compile("^https{0,1}://.*"+origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
|
||||
} else {
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
if(origin.startsWith("http")) {
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https{0,1}://", "^https{0,1}://")));
|
||||
} else {
|
||||
whiteList.add(Pattern.compile("^https{0,1}://"+origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow: %s", origin);
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
LOG.d(TAG, "Failed to add origin %s", origin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1643,4 +1781,4 @@ public class DroidGap extends PhonegapActivity {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ public class FileTransfer extends Plugin {
|
||||
return ctx.getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else if (path.startsWith("file://")) {
|
||||
return new FileInputStream(path.substring(6));
|
||||
return new FileInputStream(path.substring(7));
|
||||
}
|
||||
else {
|
||||
return new FileInputStream(path);
|
||||
|
||||
Executable
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Log to Android logging system.
|
||||
*
|
||||
* Log message can be a string or a printf formatted string with arguments.
|
||||
* See http://developer.android.com/reference/java/util/Formatter.html
|
||||
*/
|
||||
public class LOG {
|
||||
|
||||
public static final int VERBOSE = Log.VERBOSE;
|
||||
public static final int DEBUG = Log.DEBUG;
|
||||
public static final int INFO = Log.INFO;
|
||||
public static final int WARN = Log.WARN;
|
||||
public static final int ERROR = Log.ERROR;
|
||||
|
||||
// Current log level
|
||||
public static int LOGLEVEL = Log.ERROR;
|
||||
|
||||
/**
|
||||
* Set the current log level.
|
||||
*
|
||||
* @param logLevel
|
||||
*/
|
||||
public static void setLogLevel(int logLevel) {
|
||||
LOGLEVEL = logLevel;
|
||||
Log.i("PhoneGapLog", "Changing log level to " + logLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current log level.
|
||||
*
|
||||
* @param logLevel
|
||||
*/
|
||||
public static void setLogLevel(String logLevel) {
|
||||
if ("VERBOSE".equals(logLevel)) LOGLEVEL = VERBOSE;
|
||||
else if ("DEBUG".equals(logLevel)) LOGLEVEL = DEBUG;
|
||||
else if ("INFO".equals(logLevel)) LOGLEVEL = INFO;
|
||||
else if ("WARN".equals(logLevel)) LOGLEVEL = WARN;
|
||||
else if ("ERROR".equals(logLevel)) LOGLEVEL = ERROR;
|
||||
Log.i("PhoneGapLog", "Changing log level to " + logLevel + "(" + LOGLEVEL + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if log level will be logged
|
||||
*
|
||||
* @param logLevel
|
||||
* @return
|
||||
*/
|
||||
public static boolean isLoggable(int logLevel) {
|
||||
return (logLevel >= LOGLEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verbose log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
*/
|
||||
public static void v(String tag, String s) {
|
||||
if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
*/
|
||||
public static void d(String tag, String s) {
|
||||
if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Info log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
*/
|
||||
public static void i(String tag, String s) {
|
||||
if (LOG.INFO >= LOGLEVEL) Log.i(tag, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
*/
|
||||
public static void w(String tag, String s) {
|
||||
if (LOG.WARN >= LOGLEVEL) Log.w(tag, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
*/
|
||||
public static void e(String tag, String s) {
|
||||
if (LOG.ERROR >= LOGLEVEL) Log.e(tag, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verbose log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public static void v(String tag, String s, Throwable e) {
|
||||
if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public static void d(String tag, String s, Throwable e) {
|
||||
if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Info log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public static void i(String tag, String s, Throwable e) {
|
||||
if (LOG.INFO >= LOGLEVEL) Log.i(tag, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public static void w(String tag, String s, Throwable e) {
|
||||
if (LOG.WARN >= LOGLEVEL) Log.w(tag, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error log message.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public static void e(String tag, String s, Throwable e) {
|
||||
if (LOG.ERROR >= LOGLEVEL) Log.e(tag, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verbose log message with printf formatting.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param args
|
||||
*/
|
||||
public static void v(String tag, String s, Object... args) {
|
||||
if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, String.format(s, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug log message with printf formatting.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param args
|
||||
*/
|
||||
public static void d(String tag, String s, Object... args) {
|
||||
if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, String.format(s, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Info log message with printf formatting.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param args
|
||||
*/
|
||||
public static void i(String tag, String s, Object... args) {
|
||||
if (LOG.INFO >= LOGLEVEL) Log.i(tag, String.format(s, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning log message with printf formatting.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param args
|
||||
*/
|
||||
public static void w(String tag, String s, Object... args) {
|
||||
if (LOG.WARN >= LOGLEVEL) Log.w(tag, String.format(s, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Error log message with printf formatting.
|
||||
*
|
||||
* @param tag
|
||||
* @param s
|
||||
* @param args
|
||||
*/
|
||||
public static void e(String tag, String s, Object... args) {
|
||||
if (LOG.ERROR >= LOGLEVEL) Log.e(tag, String.format(s, args));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,6 +51,17 @@ public final class PluginManager {
|
||||
this.loadPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-init when loading a new HTML page into webview.
|
||||
*/
|
||||
public void reinit() {
|
||||
|
||||
// Stop plugins on current HTML page and discard
|
||||
this.onPause(false);
|
||||
this.onDestroy();
|
||||
this.plugins = new HashMap<String, IPlugin>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins from res/xml/plugins.xml
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user