mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-23 00:00:09 +08:00
Merged in latest.
This commit is contained in:
@@ -25,6 +25,9 @@ package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.phonegap.api.Command;
|
||||
import com.phonegap.api.CommandManager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
@@ -35,6 +38,7 @@ import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
@@ -50,6 +54,7 @@ import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.os.Build.*;
|
||||
import android.provider.MediaStore;
|
||||
@@ -58,6 +63,7 @@ public class DroidGap extends Activity {
|
||||
|
||||
private static final String LOG_TAG = "DroidGap";
|
||||
protected WebView appView;
|
||||
protected ImageView splashScreen;
|
||||
private LinearLayout root;
|
||||
|
||||
private Device gap;
|
||||
@@ -73,46 +79,64 @@ public class DroidGap extends Activity {
|
||||
private BrowserKey mKey;
|
||||
private AudioHandler audio;
|
||||
private CallbackServer callbackServer;
|
||||
|
||||
private CommandManager commandManager;
|
||||
|
||||
private Uri imageUri;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
||||
|
||||
LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F);
|
||||
|
||||
LinearLayout.LayoutParams webviewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 1.0F);
|
||||
|
||||
|
||||
root = new LinearLayout(this);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(Color.BLACK);
|
||||
root.setLayoutParams(containerParams);
|
||||
|
||||
appView = new WebView(this);
|
||||
appView.setLayoutParams(webviewParams);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
|
||||
splashScreen = new ImageView(this);
|
||||
splashScreen.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
1.0F));
|
||||
splashScreen.setImageResource(R.drawable.splash);
|
||||
|
||||
// root.addView(splashScreen);
|
||||
|
||||
initWebView();
|
||||
|
||||
root.addView(appView);
|
||||
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
private void initWebView() {
|
||||
appView = new WebView(DroidGap.this);
|
||||
|
||||
appView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
1.0F));
|
||||
|
||||
WebViewReflect.checkCompatibility();
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2."))
|
||||
appView.setWebChromeClient(new EclairClient(this));
|
||||
else
|
||||
{
|
||||
appView.setWebChromeClient(new GapClient(this));
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2.")) {
|
||||
appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
} else {
|
||||
appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
}
|
||||
|
||||
appView.setWebViewClient(new GapViewClient(this));
|
||||
|
||||
appView.setInitialScale(100);
|
||||
appView.setVerticalScrollBarEnabled(false);
|
||||
|
||||
appView.requestFocusFromTouch();
|
||||
|
||||
WebSettings settings = appView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
@@ -127,18 +151,15 @@ public class DroidGap extends Activity {
|
||||
WebViewReflect.setDomStorage(settings);
|
||||
// Turn off native geolocation object in browser - we use our own :)
|
||||
WebViewReflect.setGeolocationEnabled(settings, true);
|
||||
/* Bind the appView object to the gap class methods */
|
||||
// Bind the appView object to the gap class methods
|
||||
bindBrowser(appView);
|
||||
if(cupcakeStorage != null)
|
||||
cupcakeStorage.setStorage(appPackage);
|
||||
|
||||
root.addView(appView);
|
||||
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
public void invoke(String origin, boolean allow, boolean remember) {
|
||||
|
||||
// Try firing the onNativeReady event in JS. If it fails because the JS is
|
||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||
// from the JS side when the JS gets to that code.
|
||||
appView.loadUrl("javascript:try{PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,6 +247,7 @@ public class DroidGap extends Activity {
|
||||
private void bindBrowser(WebView appView)
|
||||
{
|
||||
callbackServer = new CallbackServer();
|
||||
commandManager = new CommandManager(appView, this);
|
||||
gap = new Device(appView, this);
|
||||
accel = new AccelListener(appView, this);
|
||||
launcher = new CameraLauncher(appView, this);
|
||||
@@ -238,6 +260,7 @@ public class DroidGap extends Activity {
|
||||
audio = new AudioHandler(appView, this);
|
||||
|
||||
// This creates the new javascript interfaces for PhoneGap
|
||||
appView.addJavascriptInterface(commandManager, "CommandManager");
|
||||
appView.addJavascriptInterface(gap, "DroidGap");
|
||||
appView.addJavascriptInterface(accel, "Accel");
|
||||
appView.addJavascriptInterface(launcher, "GapCam");
|
||||
@@ -249,6 +272,7 @@ public class DroidGap extends Activity {
|
||||
appView.addJavascriptInterface(mKey, "BackButton");
|
||||
appView.addJavascriptInterface(audio, "GapAudio");
|
||||
appView.addJavascriptInterface(callbackServer, "CallbackServer");
|
||||
appView.addJavascriptInterface(new SplashScreen(this), "SplashScreen");
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1."))
|
||||
{
|
||||
@@ -258,11 +282,14 @@ public class DroidGap extends Activity {
|
||||
appView.addJavascriptInterface(geo, "Geo");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadUrl(String url)
|
||||
public void loadUrl(final String url)
|
||||
{
|
||||
appView.loadUrl(url);
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
DroidGap.this.appView.loadUrl(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +310,6 @@ public class DroidGap extends Activity {
|
||||
return this.callbackServer.getPort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a hook for calling "alert" from javascript. Useful for
|
||||
* debugging your javascript.
|
||||
@@ -401,8 +427,7 @@ public class DroidGap extends Activity {
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (mKey.isBound())
|
||||
{
|
||||
@@ -428,10 +453,17 @@ public class DroidGap extends Activity {
|
||||
// This is where we launch the menu
|
||||
appView.loadUrl("javascript:keyEvent.menuTrigger()");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the splash screen from root view and adds the WebView
|
||||
*/
|
||||
public void hideSplashScreen() {
|
||||
root.removeView(splashScreen);
|
||||
root.addView(appView);
|
||||
}
|
||||
|
||||
// This is required to start the camera activity! It has to come from the previous activity
|
||||
public void startCamera()
|
||||
{
|
||||
@@ -463,11 +495,5 @@ public class DroidGap extends Activity {
|
||||
{
|
||||
launcher.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
|
||||
public WebView getView()
|
||||
{
|
||||
return this.appView;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class FileUtils {
|
||||
FileReader f_in;
|
||||
FileWriter f_out;
|
||||
|
||||
FileUtils(WebView view)
|
||||
public FileUtils(WebView view)
|
||||
{
|
||||
mView = view;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.phonegap;
|
||||
|
||||
public class SplashScreen {
|
||||
private final DroidGap gap;
|
||||
public SplashScreen(DroidGap gap) {
|
||||
this.gap = gap;
|
||||
}
|
||||
public void hide() {
|
||||
gap.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
gap.hideSplashScreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Command interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the CommandManager.
|
||||
*
|
||||
* @author davejohnson
|
||||
*
|
||||
*/
|
||||
public interface Command {
|
||||
/**
|
||||
* Executes the request and returns CommandResult.
|
||||
*
|
||||
* @param action The command to execute.
|
||||
* @param args JSONArry of arguments for the command.
|
||||
* @return A CommandResult object with a status and message.
|
||||
*/
|
||||
CommandResult execute(String action, JSONArray args);
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
void setContext(Context ctx);
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a PhoneGap app runs.
|
||||
*
|
||||
* @param webView The PhoneGap WebView
|
||||
*/
|
||||
void setView(WebView webView);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.phonegap.DroidGap;
|
||||
|
||||
/**
|
||||
* CommandManager is exposed to JavaScript in the PhoneGap WebView.
|
||||
*
|
||||
* Calling native plugin code can be done by calling CommandManager.exec(...)
|
||||
* from JavaScript.
|
||||
*
|
||||
* @author davejohnson
|
||||
*
|
||||
*/
|
||||
public final class CommandManager {
|
||||
private Command[] commands;
|
||||
|
||||
private final Context ctx;
|
||||
private final WebView app;
|
||||
|
||||
public CommandManager(WebView app, Context ctx) {
|
||||
this.ctx = ctx;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a request for execution and fulfills it by finding the appropriate
|
||||
* Java class and calling it's execute method.
|
||||
*
|
||||
* CommandManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
||||
* string is returned that will indicate if any errors have occurred when trying to find
|
||||
* or execute the class denoted by the clazz argument.
|
||||
*
|
||||
* @param clazz String containing the fully qualified class name. e.g. com.phonegap.FooBar
|
||||
* @param action String containt the action that the class is supposed to perform. This is
|
||||
* passed to the plugin execute method and it is up to the plugin developer
|
||||
* how to deal with it.
|
||||
* @param callbackId String containing the id of the callback that is execute in JavaScript if
|
||||
* this is an async plugin call.
|
||||
* @param args An Array literal string containing any arguments needed in the
|
||||
* plugin execute method.
|
||||
* @param async Boolean indicating whether the calling JavaScript code is expecting an
|
||||
* immediate return value. If true, either PhoneGap.callbackSuccess(...) or PhoneGap.callbackError(...)
|
||||
* is called once the plugin code has executed.
|
||||
* @return JSON encoded string with a response message and status.
|
||||
*/
|
||||
public String exec(final String clazz, final String action, final String callbackId,
|
||||
final String jsonArgs, final boolean async) {
|
||||
CommandResult cr = null;
|
||||
try {
|
||||
final JSONArray args = new JSONArray(jsonArgs);
|
||||
Class c = getClassByName(clazz);
|
||||
if (isPhoneGapCommand(c)) {
|
||||
// Create a new instance of the plugin and set the context and webview
|
||||
final Command plugin = (Command)c.newInstance();
|
||||
plugin.setContext(this.ctx);
|
||||
plugin.setView(this.app);
|
||||
|
||||
if (async) {
|
||||
// Run this on a different thread so that this one can return back to JS
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
CommandResult cr = plugin.execute(action, args);
|
||||
// Check the status for 0 (success) or otherwise
|
||||
if (cr.getStatus() == 0) {
|
||||
app.loadUrl(cr.toSuccessCallbackString(callbackId));
|
||||
} else {
|
||||
app.loadUrl(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
return "";
|
||||
} else {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
cr = plugin.execute(action, args);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
cr = new CommandResult(CommandResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
||||
} catch (IllegalAccessException e) {
|
||||
cr = new CommandResult(CommandResult.Status.ILLEGAL_ACCESS_EXCEPTION);
|
||||
} catch (InstantiationException e) {
|
||||
cr = new CommandResult(CommandResult.Status.INSTANTIATION_EXCEPTION);
|
||||
} catch (JSONException e) {
|
||||
cr = new CommandResult(CommandResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
// if async we have already returned at this point unless there was an error...
|
||||
if (async) {
|
||||
app.loadUrl(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
return ( cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }" );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
private Class getClassByName(final String clazz) throws ClassNotFoundException {
|
||||
return Class.forName(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interfaces that a class implements and see if it implements the
|
||||
* com.phonegap.api.Command interface.
|
||||
*
|
||||
* @param c The class to check the interfaces of.
|
||||
* @return Boolean indicating if the class implements com.phonegap.api.Command
|
||||
*/
|
||||
private boolean isPhoneGapCommand(Class c) {
|
||||
boolean isCommand = false;
|
||||
Class[] interfaces = c.getInterfaces();
|
||||
for (int j=0; j<interfaces.length; j++) {
|
||||
if (interfaces[j].getName().equals("com.phonegap.api.Command")) {
|
||||
isCommand = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isCommand;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.phonegap.api;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class CommandResult {
|
||||
private final int status;
|
||||
private final String message;
|
||||
|
||||
public CommandResult(Status status) {
|
||||
this.status = status.ordinal();
|
||||
this.message = CommandResult.StatusMessages[this.status];
|
||||
}
|
||||
|
||||
public CommandResult(Status status, String message) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getJSONString() {
|
||||
return "{ status: " + this.getStatus() + ", message: '" + URLEncoder.encode(this.getMessage()) + "' }";
|
||||
}
|
||||
|
||||
public String toSuccessCallbackString(String callbackId) {
|
||||
return "javascript:PhoneGap.callbackSuccess('"+callbackId+"', " + this.getJSONString() + " );";
|
||||
}
|
||||
|
||||
public String toErrorCallbackString(String callbackId) {
|
||||
return "javascript:PhoneGap.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
|
||||
}
|
||||
|
||||
public static String[] StatusMessages = new String[] {
|
||||
"OK",
|
||||
"Class not found",
|
||||
"Illegal access",
|
||||
"Instantiation error",
|
||||
"Malformed url",
|
||||
"IO error",
|
||||
"Invalid action",
|
||||
"JSON error"
|
||||
};
|
||||
|
||||
public enum Status {
|
||||
OK,
|
||||
CLASS_NOT_FOUND_EXCEPTION,
|
||||
ILLEGAL_ACCESS_EXCEPTION,
|
||||
INSTANTIATION_EXCEPTION,
|
||||
MALFORMED_URL_EXCEPTION,
|
||||
IO_EXCEPTION,
|
||||
INVALID_ACTION,
|
||||
JSON_EXCEPTION
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user