blob: 897b54ee492b0d15a533db87706e6f5f83d04ee1 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.LOG;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import com.amazon.android.webkit.AmazonValueCallback;
import com.amazon.android.webkit.AmazonWebViewClient;
import com.amazon.android.webkit.AmazonWebKitFactory;
import com.amazon.android.webkit.AmazonWebKitFactories;
import android.widget.LinearLayout;
/**
* This class is the main Android activity that represents the Cordova
* application. It should be extended by the user to load the specific
* html file that contains the application.
*
* As an example:
*
* package org.apache.cordova.examples;
* import android.os.Bundle;
* import org.apache.cordova.*;
*
* public class Example extends CordovaActivity {
* @Override
* public void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
* // Set properties for activity
* super.setStringProperty("loadingDialog", "Title,Message"); // show loading dialog
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
*
* // Clear cache if you want
* super.appView.clearCache(true);
*
* // Load your application
* super.setIntegerProperty("splashscreen", R.drawable.splash); // load splash.jpg image from the resource drawable directory
* super.loadUrl("file:///android_asset/www/index.html", 3000); // show splash screen 3 sec before loading app
* }
* }
*
* Properties: The application can be configured using the following properties:
*
* // Display a native loading dialog when loading app. Format for value = "Title,Message".
* // (String - default=null)
* super.setStringProperty("loadingDialog", "Wait,Loading Demo...");
*
* // Display a native loading dialog when loading sub-pages. Format for value = "Title,Message".
* // (String - default=null)
* super.setStringProperty("loadingPageDialog", "Loading page...");
*
* // Load a splash screen image from the resource drawable directory.
* // (Integer - default=0)
* super.setIntegerProperty("splashscreen", R.drawable.splash);
*
* // Set the background color.
* // (Integer - default=0 or BLACK)
* super.setIntegerProperty("backgroundColor", Color.WHITE);
*
* // Time in msec to wait before triggering a timeout error when loading
* // with super.loadUrl(). (Integer - default=20000)
* super.setIntegerProperty("loadUrlTimeoutValue", 60000);
*
* // URL to load if there's an error loading specified URL with loadUrl().
* // Should be a local URL starting with file://. (String - default=null)
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html");
*
* // Enable app to keep running in background. (Boolean - default=true)
* super.setBooleanProperty("keepRunning", false);
*
* Cordova.xml configuration:
* Cordova uses a configuration file at res/xml/cordova.xml to specify the following settings.
*
* Approved list of URLs that can be loaded into Cordova
* <access origin="http://server regexp" subdomains="true" />
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
* <log level="DEBUG" />
*
*/
public class CordovaActivity extends Activity implements CordovaInterface {
public static String TAG = "CordovaActivity";
// The webview for our app
protected CordovaWebView appView;
protected CordovaWebViewClient webViewClient;
protected LinearLayout root;
protected boolean cancelLoadUrl = false;
protected ProgressDialog spinnerDialog = null;
private final ExecutorService threadPool = Executors.newCachedThreadPool();
// The initial URL for our app
// ie http://server/path/index.html#abc?query
//private String url = 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
// Plugin to call when activity result is received
protected CordovaPlugin activityResultCallback = null;
protected boolean activityResultKeepRunning;
// Default background color for activity
// (this is not the color for the webview, which is set in HTML)
private int backgroundColor = Color.BLACK;
/*
* The variables below are used to cache some of the activity properties.
*/
// 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;
protected int splashscreenTime = 3000;
// LoadUrl timeout value in msec (default of 20 sec)
protected int loadUrlTimeoutValue = 20000;
// Keep app running when pause is received. (default = true)
// If true, then the JavaScript and native code continue to run in the background
// when another application (activity) is started.
protected boolean keepRunning = true;
private int lastRequestCode;
private Object responseCode;
private Intent lastIntent;
private Object lastResponseCode;
private String initCallbackClass;
private Object LOG_TAG;
private static boolean sFactoryInit = false;
private AmazonWebKitFactory factory = null;
private static final String AMAZON_WEBVIEW_LIB_PACKAGE="com.amazon.webview";
private static final String ANDROID_WEBKIT_FACTORY_MISSING = "AndroidWebKit classes are missing. Please copy android_interface.jar from AmazonWebView SDK to app's libs folder and then rebuild the app.";
private static final String ERROR_DIALOG_TITLE = "Application Error";
private static final String ERROR_DIALOG_OK_BUTTON = "OK";
/**
* Sets the authentication token.
*
* @param authenticationToken
* @param host
* @param realm
*/
public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
if (this.appView != null && this.appView.viewClient != null) {
this.appView.viewClient.setAuthenticationToken(authenticationToken, host, realm);
}
}
/**
* Removes the authentication token.
*
* @param host
* @param realm
*
* @return the authentication token or null if did not exist
*/
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
if (this.appView != null && this.appView.viewClient != null) {
return this.appView.viewClient.removeAuthenticationToken(host, realm);
}
return null;
}
/**
* Gets the authentication token.
*
* In order it tries:
* 1- host + realm
* 2- host
* 3- realm
* 4- no host, no realm
*
* @param host
* @param realm
*
* @return the authentication token
*/
public AuthenticationToken getAuthenticationToken(String host, String realm) {
if (this.appView != null && this.appView.viewClient != null) {
return this.appView.viewClient.getAuthenticationToken(host, realm);
}
return null;
}
/**
* Clear all authentication tokens.
*/
public void clearAuthenticationTokens() {
if (this.appView != null && this.appView.viewClient != null) {
this.appView.viewClient.clearAuthenticationTokens();
}
}
/**
* Called when the activity is first created.
*
* @param savedInstanceState
*/
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
Config.init(this);
LOG.d(TAG, "CordovaActivity.onCreate()");
super.onCreate(savedInstanceState);
try {
if (!sFactoryInit) {
// To override the default factory (Chromium-based or Android WebKit factory), uncomment one of the following lines:
// AmazonWebKitFactories.setDefaultFactory("com.amazon.android.webkit.embedded.EmbeddedWebKitFactory");
// AmazonWebKitFactories.setDefaultFactory("com.amazon.android.webkit.android.AndroidWebKitFactory");
factory = AmazonWebKitFactories.getDefaultFactory();
if (factory.isRenderProcess(this)) {
return; // Do nothing if this is on render process
}
factory.setNativeLibraryPackage(AMAZON_WEBVIEW_LIB_PACKAGE);
factory.initialize(this);
factory.disableDeveloperTools();
// factory configuration
factory.getCookieManager().setAcceptCookie(true);
sFactoryInit = true;
} else {
factory = AmazonWebKitFactories.getDefaultFactory();
}
} catch (ExceptionInInitializerError e) {
LOG.e(TAG, "WebKit factory initialization failed. Make sure you have android_interface.jar in libs folder.");
displayError(ERROR_DIALOG_TITLE, ANDROID_WEBKIT_FACTORY_MISSING, ERROR_DIALOG_OK_BUTTON, true);
} catch (NoClassDefFoundError e) {
LOG.e(TAG, "WebKit factory initialization failed. Make sure you have android_interface.jar in libs folder.");
displayError(ERROR_DIALOG_TITLE, ANDROID_WEBKIT_FACTORY_MISSING, ERROR_DIALOG_OK_BUTTON, true);
} catch (Exception e) {
LOG.e(TAG, "WebKit factory initialization failed.");
displayError(ERROR_DIALOG_TITLE, ANDROID_WEBKIT_FACTORY_MISSING, ERROR_DIALOG_OK_BUTTON, true);
}
if (savedInstanceState != null)
{
initCallbackClass = savedInstanceState.getString("callbackClass");
}
if (!this.getBooleanProperty("ShowTitle", false))
{
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
}
if (this.getBooleanProperty("SetFullscreen", false))
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
else
{
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!
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
root = new LinearLayoutSoftKeyboardDetect(this, width, height);
root.setOrientation(LinearLayout.VERTICAL);
root.setBackgroundColor(this.backgroundColor);
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
// Setup the hardware volume controls to handle volume control
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
/**
* Get the Android activity.
*
* @return
*/
public Activity getActivity() {
return this;
}
/**
* Get the WebKit factory.
*
* @return
*/
public AmazonWebKitFactory getFactory() {
return this.factory;
}
/**
* Construct the default web view object.
*
* This is intended to be overridable by subclasses of CordovaIntent which
* require a more specialized web view.
*/
protected CordovaWebView makeWebView() {
return new CordovaWebView(CordovaActivity.this);
}
/**
* Construct the client for the default web view object.
*
* This is intended to be overridable by subclasses of CordovaIntent which
* require a more specialized web view.
*
* @param webView the default constructed web view object
*/
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
return new CordovaWebViewClient(this, webView);
} else {
return new IceCreamCordovaWebViewClient(this, webView);
}
}
/**
* Construct the chrome client for the default web view object.
*
* This is intended to be overridable by subclasses of CordovaIntent which
* require a more specialized web view.
*
* @param webView the default constructed web view object
*/
protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
return new CordovaChromeClient(this, webView);
}
/**
* Create and initialize web container with default web view objects.
*/
public void init() {
if (factory != null) {
CordovaWebView webView = makeWebView();
this.init(webView, makeWebViewClient(webView), makeChromeClient(webView));
}
}
/**
* Initialize web container with web view objects.
*
* @param webView
* @param webViewClient
* @param webChromeClient
*/
@SuppressLint("NewApi")
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
LOG.d(TAG, "CordovaActivity.init()");
// Set up web container
this.appView = webView;
this.appView.setId(100);
this.appView.setWebViewClient(webViewClient);
this.appView.setWebChromeClient(webChromeClient);
webViewClient.setWebView(this.appView);
webChromeClient.setWebView(this.appView);
this.appView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
1.0F));
if (this.getBooleanProperty("DisallowOverscroll", false)) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
this.appView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
}
}
// Add web view but make it invisible while loading URL
this.appView.setVisibility(View.INVISIBLE);
this.root.addView(this.appView);
setContentView(this.root);
// Clear cancel flag
this.cancelLoadUrl = false;
}
/**
* Load the url into the webview.
*
* @param url
*/
public void loadUrl(String url) {
// Init web view if not already done
if (this.appView == null) {
this.init();
// check again if its still null then exit. Something is wrong - factory is not initialized or webview is not created
if (this.appView == null) {
return;
}
}
this.splashscreenTime = this.getIntegerProperty("SplashScreenDelay", this.splashscreenTime);
if(this.splashscreenTime > 0)
{
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
if(this.splashscreen != 0)
{
this.showSplashScreen(this.splashscreenTime);
}
}
// Set backgroundColor
this.backgroundColor = this.getIntegerProperty("BackgroundColor", Color.BLACK);
this.root.setBackgroundColor(this.backgroundColor);
// If keepRunning
this.keepRunning = this.getBooleanProperty("KeepRunning", true);
//Check if the view is attached to anything
if(appView.getParent() != null)
{
// Then load the spinner
this.loadSpinner();
}
//Load the correct splashscreen
if(this.splashscreen != 0)
{
this.appView.loadUrl(url, this.splashscreenTime);
}
else
{
this.appView.loadUrl(url);
}
}
/**
* Load the url into the webview after waiting for period of time.
* This is used to display the splashscreen for certain amount of time.
*
* @param url
* @param time The number of ms to wait before loading webview
*/
public void loadUrl(final String url, int time) {
this.splashscreenTime = time;
this.loadUrl(url);
/*
// Init web view if not already done
if (this.appView == null) {
this.init();
}
this.splashscreenTime = time;
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
this.showSplashScreen(this.splashscreenTime);
this.appView.loadUrl(url, time);
*/
}
/*
* Load the spinner
*/
void loadSpinner() {
// If loadingDialog property, then show the App loading dialog for first page of app
String loading = null;
if ((this.appView == null) || !this.appView.canGoBack()) {
loading = this.getStringProperty("LoadingDialog", null);
}
else {
loading = this.getStringProperty("LoadingPageDialog", null);
}
if (loading != null) {
String title = "";
String message = "Loading Application...";
if (loading.length() > 0) {
int comma = loading.indexOf(',');
if (comma > 0) {
title = loading.substring(0, comma);
message = loading.substring(comma + 1);
}
else {
title = "";
message = loading;
}
}
this.spinnerStart(title, message);
}
}
/**
* Cancel loadUrl before it has been loaded.
*/
// TODO NO-OP
@Deprecated
public void cancelLoadUrl() {
this.cancelLoadUrl = true;
}
/**
* Clear the resource cache.
*/
public void clearCache() {
if (this.appView == null) {
this.init();
}
this.appView.clearCache(true);
}
/**
* Clear web history in this web view.
*/
public void clearHistory() {
this.appView.clearHistory();
}
/**
* Go to previous page in history. (We manage our own history)
*
* @return true if we went back, false if we are already at top
*/
public boolean backHistory() {
if (this.appView != null) {
return appView.backHistory();
}
return false;
}
@Override
/**
* Called by the system when the device configuration changes while your activity is running.
*
* @param Configuration newConfig
*/
public void onConfigurationChanged(Configuration newConfig) {
//don't reload the current page when the orientation is changed
super.onConfigurationChanged(newConfig);
}
/**
* Get boolean property for activity.
*
* @param name
* @param defaultValue
* @return
*/
public boolean getBooleanProperty(String name, boolean defaultValue) {
Bundle bundle = this.getIntent().getExtras();
if (bundle == null) {
return defaultValue;
}
name = name.toLowerCase(Locale.getDefault());
Boolean p;
try {
p = (Boolean) bundle.get(name);
} catch (ClassCastException e) {
String s = bundle.get(name).toString();
if ("true".equals(s)) {
p = true;
}
else {
p = false;
}
}
if (p == null) {
return defaultValue;
}
return p.booleanValue();
}
/**
* Get int property for activity.
*
* @param name
* @param defaultValue
* @return
*/
public int getIntegerProperty(String name, int defaultValue) {
Bundle bundle = this.getIntent().getExtras();
if (bundle == null) {
return defaultValue;
}
name = name.toLowerCase(Locale.getDefault());
Integer p;
try {
p = (Integer) bundle.get(name);
} catch (ClassCastException e) {
p = Integer.parseInt(bundle.get(name).toString());
}
if (p == null) {
return defaultValue;
}
return p.intValue();
}
/**
* Get string property for activity.
*
* @param name
* @param defaultValue
* @return
*/
public String getStringProperty(String name, String defaultValue) {
Bundle bundle = this.getIntent().getExtras();
if (bundle == null) {
return defaultValue;
}
name = name.toLowerCase(Locale.getDefault());
String p = bundle.getString(name);
if (p == null) {
return defaultValue;
}
return p;
}
/**
* Get double property for activity.
*
* @param name
* @param defaultValue
* @return
*/
public double getDoubleProperty(String name, double defaultValue) {
Bundle bundle = this.getIntent().getExtras();
if (bundle == null) {
return defaultValue;
}
name = name.toLowerCase(Locale.getDefault());
Double p;
try {
p = (Double) bundle.get(name);
} catch (ClassCastException e) {
p = Double.parseDouble(bundle.get(name).toString());
}
if (p == null) {
return defaultValue;
}
return p.doubleValue();
}
/**
* Set boolean property on activity.
*
* @param name
* @param value
*/
public void setBooleanProperty(String name, boolean value) {
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name.toLowerCase(), value);
}
/**
* Set int property on activity.
*
* @param name
* @param value
*/
public void setIntegerProperty(String name, int value) {
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name.toLowerCase(), value);
}
/**
* Set string property on activity.
*
* @param name
* @param value
*/
public void setStringProperty(String name, String value) {
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name.toLowerCase(), value);
}
/**
* Set double property on activity.
*
* @param name
* @param value
*/
public void setDoubleProperty(String name, double value) {
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name.toLowerCase(), value);
}
@Override
/**
* Called when the system is about to start resuming a previous activity.
*/
protected void onPause() {
super.onPause();
LOG.d(TAG, "Paused the application!");
// Don't process pause if shutting down, since onDestroy() will be called
if (this.activityState == ACTIVITY_EXITING) {
return;
}
if (this.appView == null) {
return;
}
else
{
this.appView.handlePause(this.keepRunning);
}
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
}
@Override
/**
* Called when the activity receives a new intent
**/
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//Forward to plugins
if (this.appView != null)
this.appView.onNewIntent(intent);
}
@Override
/**
* Called when the activity will start interacting with the user.
*/
protected void onResume() {
super.onResume();
//Reload the configuration
Config.init(this);
LOG.d(TAG, "Resuming the App");
//Code to test CB-3064
String errorUrl = this.getStringProperty("ErrorUrl", null);
LOG.d(TAG, "CB-3064: The errorUrl is " + errorUrl);
if (this.activityState == ACTIVITY_STARTING) {
this.activityState = ACTIVITY_RUNNING;
return;
}
if (this.appView == null) {
return;
}
this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning);
// If app doesn't want to run in background
if (!this.keepRunning || this.activityResultKeepRunning) {
// Restore multitasking state
if (this.activityResultKeepRunning) {
this.keepRunning = this.activityResultKeepRunning;
this.activityResultKeepRunning = false;
}
}
}
@Override
/**
* The final call you receive before your activity is destroyed.
*/
public void onDestroy() {
LOG.d(TAG, "CordovaActivity.onDestroy()");
super.onDestroy();
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
if (this.appView != null) {
appView.handleDestroy();
}
else {
this.activityState = ACTIVITY_EXITING;
}
}
/**
* Send a message to all plugins.
*
* @param id The message id
* @param data The message data
*/
public void postMessage(String id, Object data) {
if (this.appView != null) {
this.appView.postMessage(id, data);
}
}
/**
* @deprecated
* Add services to res/xml/plugins.xml instead.
*
* Add a class that implements a service.
*
* @param serviceType
* @param className
*/
public void addService(String serviceType, String className) {
if (this.appView != null && this.appView.pluginManager != null) {
this.appView.pluginManager.addService(serviceType, className);
}
}
/**
* Send JavaScript statement back to JavaScript.
* (This is a convenience method)
*
* @param statement
*/
public void sendJavascript(String statement) {
if (this.appView != null) {
this.appView.jsMessageQueue.addJavaScript(statement);
}
}
/**
* Show the spinner. Must be called from the UI thread.
*
* @param title Title of the dialog
* @param message The message of the dialog
*/
public void spinnerStart(final String title, final String message) {
if (this.spinnerDialog != null) {
this.spinnerDialog.dismiss();
this.spinnerDialog = null;
}
final CordovaActivity me = this;
this.spinnerDialog = ProgressDialog.show(CordovaActivity.this, title, message, true, true,
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
me.spinnerDialog = null;
}
});
}
/**
* Stop spinner - Must be called from UI thread
*/
public void spinnerStop() {
if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
this.spinnerDialog.dismiss();
this.spinnerDialog = null;
}
}
/**
* End this activity by calling finish for activity
*/
public void endActivity() {
this.activityState = ACTIVITY_EXITING;
super.finish();
}
/**
* Launch an activity for which you would like a result when it finished. When this activity exits,
* your onActivityResult() method will be called.
*
* @param command The command object
* @param intent The intent to start
* @param requestCode The request code that is passed to callback to identify the activity
*/
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
this.activityResultCallback = command;
this.activityResultKeepRunning = this.keepRunning;
// If multitasking turned on, then disable it for activities that return results
if (command != null) {
this.keepRunning = false;
}
// Start activity
super.startActivityForResult(intent, requestCode);
}
@Override
/**
* Called when an activity you launched exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
LOG.d(TAG, "Incoming Result");
super.onActivityResult(requestCode, resultCode, intent);
Log.d(TAG, "Request code = " + requestCode);
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
AmazonValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
Log.d(TAG, "did we get here?");
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
Log.d(TAG, "result = " + result);
// Uri filepath = Uri.parse("file://" + FileUtils.getRealPathFromURI(result, this));
// Log.d(TAG, "result = " + filepath);
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
CordovaPlugin callback = this.activityResultCallback;
if(callback == null && initCallbackClass != null) {
// The application was restarted, but had defined an initial callback
// before being shut down.
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
callback = this.activityResultCallback;
}
if(callback != null) {
LOG.d(TAG, "We have a callback to send this result to");
callback.onActivityResult(requestCode, resultCode, intent);
}
}
public void setActivityResultCallback(CordovaPlugin plugin) {
this.activityResultCallback = plugin;
}
/**
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
* The errorCode parameter corresponds to one of the ERROR_* constants.
*
* @param errorCode The error code corresponding to an ERROR_* value.
* @param description A String describing the error.
* @param failingUrl The url that failed to load.
*/
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
final CordovaActivity me = this;
// If errorUrl specified, then load it
final String errorUrl = me.getStringProperty("errorUrl", null);
if ((errorUrl != null) && (errorUrl.startsWith("file://") || Config.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
// Load URL on UI thread
me.runOnUiThread(new Runnable() {
public void run() {
// Stop "app loading" spinner if showing
me.spinnerStop();
me.appView.showWebPage(errorUrl, false, true, null);
}
});
}
// If not, then display error dialog
else {
final boolean exit = !(errorCode == AmazonWebViewClient.ERROR_HOST_LOOKUP);
me.runOnUiThread(new Runnable() {
public void run() {
if (exit) {
me.appView.setVisibility(View.GONE);
me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
}
}
});
}
}
/**
* Display an error dialog and optionally exit application.
*
* @param title
* @param message
* @param button
* @param exit
*/
public void displayError(final String title, final String message, final String button, final boolean exit) {
final CordovaActivity me = this;
me.runOnUiThread(new Runnable() {
public void run() {
try {
AlertDialog.Builder dlg = new AlertDialog.Builder(me);
dlg.setMessage(message);
dlg.setTitle(title);
dlg.setCancelable(false);
dlg.setPositiveButton(button,
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (exit) {
endActivity();
}
}
});
dlg.create();
dlg.show();
} catch (Exception e) {
finish();
}
}
});
}
/**
* Determine if URL is in approved list of URLs to load.
*
* @param url
* @return
*/
public boolean isUrlWhiteListed(String url) {
return Config.isUrlWhiteListed(url);
}
/*
* Hook in Cordova for menu plugins
*
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
this.postMessage("onCreateOptionsMenu", menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
this.postMessage("onPrepareOptionsMenu", menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
this.postMessage("onOptionsItemSelected", item);
return true;
}
/**
* Get Activity context.
*
* @return
*/
public Context getContext() {
LOG.d(TAG, "This will be deprecated December 2012");
return this;
}
/**
* Load the specified URL in the Cordova webview or a new browser instance.
*
* NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
*
* @param url The url to load.
* @param openExternal Load url in browser instead of Cordova webview.
* @param clearHistory Clear the history stack, so new page becomes top of history
* @param params Parameters for new app
*/
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
if (this.appView != null) {
appView.showWebPage(url, openExternal, clearHistory, params);
}
}
protected Dialog splashDialog;
/**
* Removes the Dialog that displays the splash screen
*/
public void removeSplashScreen() {
if (splashDialog != null && splashDialog.isShowing()) {
splashDialog.dismiss();
splashDialog = null;
}
}
/**
* Shows the splash screen over the full Activity
*/
@SuppressWarnings("deprecation")
protected void showSplashScreen(final int time) {
final CordovaActivity that = this;
Runnable runnable = new Runnable() {
public void run() {
// Get reference to display
Display display = getWindowManager().getDefaultDisplay();
// Create the layout for the dialog
LinearLayout root = new LinearLayout(that.getActivity());
root.setMinimumHeight(display.getHeight());
root.setMinimumWidth(display.getWidth());
root.setOrientation(LinearLayout.VERTICAL);
root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
root.setBackgroundResource(that.splashscreen);
// Create and show the dialog
splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
// check to see if the splash screen should be full screen
if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
splashDialog.setContentView(root);
splashDialog.setCancelable(false);
splashDialog.show();
// Set Runnable to remove splash screen just in case
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
removeSplashScreen();
}
}, time);
}
};
this.runOnUiThread(runnable);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
if (appView != null && (appView.isCustomViewShowing() || appView.getFocusedChild() != null ) &&
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
return appView.onKeyUp(keyCode, event);
} else {
return super.onKeyUp(keyCode, event);
}
}
/*
* Android 2.x needs to be able to check where the cursor is. Android 4.x does not
*
* (non-Javadoc)
* @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
//Determine if the focus is on the current view or not
if (appView != null && appView.getFocusedChild() != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
return appView.onKeyDown(keyCode, event);
}
else
return super.onKeyDown(keyCode, event);
}
/**
* Called when a message is sent to plugin.
*
* @param id The message id
* @param data The message data
* @return Object or null
*/
public Object onMessage(String id, Object data) {
LOG.d(TAG, "onMessage(" + id + "," + data + ")");
if ("splashscreen".equals(id)) {
if ("hide".equals(data.toString())) {
this.removeSplashScreen();
}
else {
// If the splash dialog is showing don't try to show it again
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
this.showSplashScreen(this.splashscreenTime);
}
}
}
else if ("spinner".equals(id)) {
if ("stop".equals(data.toString())) {
this.spinnerStop();
this.appView.setVisibility(View.VISIBLE);
}
}
else if ("onReceivedError".equals(id)) {
JSONObject d = (JSONObject) data;
try {
this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));
} catch (JSONException e) {
e.printStackTrace();
}
}
else if ("exit".equals(id)) {
this.endActivity();
}
return null;
}
public ExecutorService getThreadPool() {
return threadPool;
}
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if(this.activityResultCallback != null)
{
String cClass = this.activityResultCallback.getClass().getName();
outState.putString("callbackClass", cClass);
}
}
}