2010-10-19 04:31:16 +08:00
/ *
* 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.
2009-04-02 07:56:43 +08:00
*
2010-10-19 04:31:16 +08:00
* Copyright ( c ) 2005 - 2010 , Nitobi Software Inc .
* Copyright ( c ) 2010 , IBM Corporation
2009-04-02 07:56:43 +08:00
* /
2010-10-19 04:31:16 +08:00
package com.phonegap ;
2009-04-02 07:56:43 +08:00
2010-11-12 12:24:20 +08:00
import org.json.JSONArray ;
2009-04-02 07:56:43 +08:00
import android.app.AlertDialog ;
import android.content.Context ;
2009-12-17 03:09:32 +08:00
import android.content.DialogInterface ;
2009-07-18 07:23:18 +08:00
import android.content.Intent ;
2009-04-02 07:56:43 +08:00
import android.content.res.Configuration ;
2010-06-05 05:54:16 +08:00
import android.graphics.Bitmap ;
2009-11-25 08:51:23 +08:00
import android.graphics.Color ;
2010-04-24 07:18:11 +08:00
import android.net.Uri ;
2009-04-02 07:56:43 +08:00
import android.os.Bundle ;
import android.util.Log ;
2009-12-17 04:50:08 +08:00
import android.view.KeyEvent ;
2009-11-25 08:51:23 +08:00
import android.view.ViewGroup ;
2009-04-02 07:56:43 +08:00
import android.view.Window ;
import android.view.WindowManager ;
import android.webkit.JsResult ;
2010-11-16 06:32:55 +08:00
import android.webkit.WebBackForwardList ;
2009-04-02 07:56:43 +08:00
import android.webkit.WebChromeClient ;
2010-11-16 06:32:55 +08:00
import android.webkit.WebHistoryItem ;
2009-11-10 09:22:36 +08:00
import android.webkit.WebSettings ;
2009-11-26 09:25:16 +08:00
import android.webkit.WebStorage ;
2009-04-02 07:56:43 +08:00
import android.webkit.WebView ;
2010-04-24 07:18:11 +08:00
import android.webkit.WebViewClient ;
2010-07-08 06:18:14 +08:00
import android.webkit.GeolocationPermissions.Callback ;
2009-11-13 02:51:22 +08:00
import android.webkit.WebSettings.LayoutAlgorithm ;
2009-11-25 08:51:23 +08:00
import android.widget.LinearLayout ;
2010-11-06 02:10:51 +08:00
import com.phonegap.api.Plugin ;
import com.phonegap.api.PluginManager ;
import com.phonegap.api.PhonegapActivity ;
2009-04-02 07:56:43 +08:00
2010-08-27 22:31:57 +08:00
/ * *
* This class is the main Android activity that represents the PhoneGap
* application . It should be extended by the user to load the specific
* html file that contains the application .
*
* As an example :
*
* package com.phonegap.examples ;
* import android.app.Activity ;
* import android.os.Bundle ;
* import com.phonegap.* ;
*
* public class Examples extends DroidGap {
* @Override
* public void onCreate ( Bundle savedInstanceState ) {
* super . onCreate ( savedInstanceState ) ;
2010-11-13 12:38:27 +08:00
*
2010-11-12 01:34:12 +08:00
* // Set properties for activity
2010-11-12 12:24:20 +08:00
* super . setProperty ( " loadingDialog " , " Title,Message " ) ; // show loading dialog
2010-11-13 12:38:27 +08:00
* super . setProperty ( " errorUrl " , " file:///android_asset/www/error.html " ) ; // if error loading file in super.loadUrl().
*
* // Initialize activity
* super . init ( ) ;
2010-11-12 01:34:12 +08:00
*
* // Add your plugins here or in JavaScript
* super . addService ( " MyService " , " com.phonegap.examples.MyService " ) ;
*
* // Clear cache if you want
* super . appView . clearCache ( true ) ;
*
* // Load your application
2010-11-13 12:38:27 +08:00
* super . loadSplashscreen ( " file:///android_asset/www/splash.html " ) ;
* super . loadUrl ( " file:///android_asset/www/index.html " , 3000 ) ; // show splash screen 3 sec before loading app
2010-08-27 22:31:57 +08:00
* }
* }
2010-11-13 12:38:27 +08:00
*
* Properties : The application can be configured using the following properties :
* super . setProperty ( " hideLoadingDialogOnPage " , true ) ;
* super . setProperty ( " loadInWebView " , true ) ;
* super . setProperty ( " splashscreen " , R . drawable . splash ) ;
* super . setProperty ( " loadUrlTimeoutValue " , 60000 ) ;
* super . setProperty ( " loadingDialog " , " Wait,Loading Demo... " ) ;
* super . setProperty ( " errorUrl " , " file:///android_asset/www/error.html " ) ;
2010-11-15 07:33:06 +08:00
* super . setProperty ( " keepRunning " , false ) ;
2010-11-13 12:38:27 +08:00
*
* Splash screens :
* There are 2 ways to display a splash screen .
* 1 . Specify an image file from the resource drawable directory . If there
* is an image called splash . jpg , then you would call :
* super . setProperty ( " splashscreen " , R . drawable . splash
* 2 . Specify an HTML file that contains the splash screen :
* super . loadSplashScreen ( " file:///android_asset/www/splash.html " ) ;
2010-08-27 22:31:57 +08:00
* /
2010-11-06 02:10:51 +08:00
public class DroidGap extends PhonegapActivity {
2010-09-07 02:13:09 +08:00
2010-11-13 12:38:27 +08:00
// The webview for our app
protected WebView appView ;
2010-09-03 00:27:48 +08:00
2010-11-13 12:38:27 +08:00
private LinearLayout root ;
private BrowserKey mKey ;
public CallbackServer callbackServer ;
2010-10-08 22:18:10 +08:00
protected PluginManager pluginManager ;
2010-09-07 02:13:09 +08:00
2010-11-13 12:38:27 +08:00
// The initial URL for our app
private String url ;
// The base of the initial URL for our app
private String baseUrl ;
// Plugin to call when activity result is received
private Plugin activityResultCallback = null ;
2010-09-04 03:01:24 +08:00
2010-11-16 06:32:55 +08:00
// URL of the splash screen that is currently showing
private String splashScreenShowing = null ;
2010-11-13 12:38:27 +08:00
// Flag indicates that a loadUrl timeout occurred
private boolean loadUrlTimeout = false ;
/ *
* The variables below are used to cache some of the activity properties .
* /
// Flag indicates that "app loading" dialog should be hidden once page is loaded.
// The default is to hide it once PhoneGap JavaScript code has initialized.
protected boolean hideLoadingDialogOnPageLoad = false ;
2010-11-12 01:34:12 +08:00
2010-11-13 12:38:27 +08:00
// Flag indicates that a URL navigated to from PhoneGap app should be loaded into same webview
// instead of being loaded into the web browser.
2010-11-12 01:34:12 +08:00
protected boolean loadInWebView = false ;
2010-11-13 12:38:27 +08:00
// Draw a splash screen using an image located in the drawable resource directory.
// This is not the same as calling super.loadSplashscreen(url)
2010-11-12 05:59:35 +08:00
protected int splashscreen = 0 ;
2010-11-13 12:38:27 +08:00
// LoadUrl timeout value in msec (default of 20 sec)
protected int loadUrlTimeoutValue = 20000 ;
2010-11-15 07:33:06 +08:00
// 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 ;
2010-11-13 12:38:27 +08:00
2010-09-04 03:14:28 +08:00
/ * *
* Called when the activity is first created .
*
* @param savedInstanceState
* /
2010-11-12 01:34:12 +08:00
@Override
2009-04-02 07:56:43 +08:00
public void onCreate ( Bundle savedInstanceState ) {
2010-11-12 04:00:56 +08:00
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!
root = new LinearLayout ( this ) ;
root . setOrientation ( LinearLayout . VERTICAL ) ;
root . setBackgroundColor ( Color . BLACK ) ;
root . setLayoutParams ( new LinearLayout . LayoutParams ( ViewGroup . LayoutParams . FILL_PARENT ,
ViewGroup . LayoutParams . FILL_PARENT , 0 . 0F ) ) ;
// If url was passed in to intent, then init webview, which will load the url
Bundle bundle = this . getIntent ( ) . getExtras ( ) ;
if ( bundle ! = null ) {
String url = bundle . getString ( " url " ) ;
if ( url ! = null ) {
this . init ( ) ;
}
}
}
2010-09-10 00:01:56 +08:00
/ * *
* Create and initialize web container .
* /
2010-11-12 01:34:12 +08:00
public void init ( ) {
2010-07-31 03:23:55 +08:00
2010-09-10 00:01:56 +08:00
// Create web container
this . appView = new WebView ( DroidGap . this ) ;
this . appView . setLayoutParams ( new LinearLayout . LayoutParams (
2010-07-31 03:23:55 +08:00
ViewGroup . LayoutParams . FILL_PARENT ,
ViewGroup . LayoutParams . FILL_PARENT ,
1 . 0F ) ) ;
2009-11-26 09:25:16 +08:00
WebViewReflect . checkCompatibility ( ) ;
2010-07-31 03:23:55 +08:00
if ( android . os . Build . VERSION . RELEASE . startsWith ( " 2. " ) ) {
2010-09-10 00:01:56 +08:00
this . appView . setWebChromeClient ( new EclairClient ( DroidGap . this ) ) ;
2010-08-27 22:31:57 +08:00
}
else {
2010-09-10 00:01:56 +08:00
this . appView . setWebChromeClient ( new GapClient ( DroidGap . this ) ) ;
2009-12-09 06:08:48 +08:00
}
2010-07-07 02:43:25 +08:00
2010-09-10 00:01:56 +08:00
this . appView . setWebViewClient ( new GapViewClient ( this ) ) ;
2010-08-25 02:19:22 +08:00
2010-09-10 00:01:56 +08:00
this . appView . setInitialScale ( 100 ) ;
this . appView . setVerticalScrollBarEnabled ( false ) ;
this . appView . requestFocusFromTouch ( ) ;
2010-08-18 02:26:39 +08:00
2010-09-10 00:01:56 +08:00
// Enable JavaScript
WebSettings settings = this . appView . getSettings ( ) ;
2009-11-10 09:22:36 +08:00
settings . setJavaScriptEnabled ( true ) ;
settings . setJavaScriptCanOpenWindowsAutomatically ( true ) ;
2009-11-13 02:51:22 +08:00
settings . setLayoutAlgorithm ( LayoutAlgorithm . NORMAL ) ;
2009-11-26 09:25:16 +08:00
2010-09-10 00:01:56 +08:00
// Enable database
Package pack = this . getClass ( ) . getPackage ( ) ;
String appPackage = pack . getName ( ) ;
2009-11-26 09:25:16 +08:00
WebViewReflect . setStorage ( settings , true , " /data/data/ " + appPackage + " /app_database/ " ) ;
2010-09-10 00:01:56 +08:00
// Enable DOM storage
2010-03-03 03:14:20 +08:00
WebViewReflect . setDomStorage ( settings ) ;
2010-09-10 00:01:56 +08:00
// Enable built-in geolocation
2010-07-08 06:18:14 +08:00
WebViewReflect . setGeolocationEnabled ( settings , true ) ;
2010-09-10 00:01:56 +08:00
// Bind PhoneGap objects to JavaScript
this . bindBrowser ( this . appView ) ;
2010-11-12 01:34:12 +08:00
// Add web view
root . addView ( this . appView ) ;
setContentView ( root ) ;
// Handle activity parameters
this . handleActivityParameters ( ) ;
2010-07-08 06:18:14 +08:00
}
2010-11-12 01:34:12 +08:00
2010-11-12 04:00:56 +08:00
/ * *
* Bind PhoneGap objects to JavaScript .
*
* @param appView
* /
private void bindBrowser ( WebView appView ) {
this . callbackServer = new CallbackServer ( ) ;
this . pluginManager = new PluginManager ( appView , this ) ;
this . mKey = new BrowserKey ( appView , this ) ;
// This creates the new javascript interfaces for PhoneGap
appView . addJavascriptInterface ( this . pluginManager , " PluginManager " ) ;
appView . addJavascriptInterface ( this . mKey , " BackButton " ) ;
appView . addJavascriptInterface ( this . callbackServer , " CallbackServer " ) ;
this . addService ( " Geolocation " , " com.phonegap.GeoBroker " ) ;
this . addService ( " Device " , " com.phonegap.Device " ) ;
this . addService ( " Accelerometer " , " com.phonegap.AccelListener " ) ;
this . addService ( " Compass " , " com.phonegap.CompassListener " ) ;
this . addService ( " Media " , " com.phonegap.AudioHandler " ) ;
this . addService ( " Camera " , " com.phonegap.CameraLauncher " ) ;
this . addService ( " Contacts " , " com.phonegap.ContactManager " ) ;
this . addService ( " Crypto " , " com.phonegap.CryptoHandler " ) ;
this . addService ( " File " , " com.phonegap.FileUtils " ) ;
this . addService ( " Location " , " com.phonegap.GeoBroker " ) ; // Always add Location, even though it is built-in on 2.x devices. Let JavaScript decide which one to use.
this . addService ( " Network Status " , " com.phonegap.NetworkManager " ) ;
this . addService ( " Notification " , " com.phonegap.Notification " ) ;
this . addService ( " Storage " , " com.phonegap.Storage " ) ;
this . addService ( " Temperature " , " com.phonegap.TempListener " ) ;
}
2010-11-12 01:34:12 +08:00
/ * *
* Look at activity parameters and process them .
* /
private void handleActivityParameters ( ) {
2010-11-12 05:59:35 +08:00
// If spashscreen
this . splashscreen = this . getProperty ( " splashscreen " , 0 ) ;
if ( this . splashscreen ! = 0 ) {
2010-11-12 11:56:56 +08:00
appView . setBackgroundColor ( 0 ) ;
appView . setBackgroundResource ( splashscreen ) ;
2010-11-12 04:00:56 +08:00
}
2010-09-10 00:01:56 +08:00
2010-11-12 04:00:56 +08:00
// If hideLoadingDialogOnPageLoad
this . hideLoadingDialogOnPageLoad = this . getProperty ( " hideLoadingDialogOnPageLoad " , false ) ;
2010-11-12 01:34:12 +08:00
2010-11-12 04:00:56 +08:00
// If loadInWebView
this . loadInWebView = this . getProperty ( " loadInWebView " , false ) ;
2010-11-12 01:34:12 +08:00
2010-11-13 12:38:27 +08:00
// If loadUrlTimeoutValue
int timeout = this . getProperty ( " loadUrlTimeoutValue " , 0 ) ;
if ( timeout > 0 ) {
this . loadUrlTimeoutValue = timeout ;
}
2010-11-15 07:33:06 +08:00
// If keepRunning
this . keepRunning = this . getProperty ( " keepRunning " , true ) ;
2010-11-13 12:38:27 +08:00
2010-11-12 04:00:56 +08:00
// If url specified, then load it
String url = this . getProperty ( " url " , null ) ;
if ( url ! = null ) {
System . out . println ( " Loading initial URL= " + url ) ;
this . loadUrl ( url ) ;
}
}
/ * *
* Load the url into the webview .
*
* @param url
* /
public void loadUrl ( final String url ) {
System . out . println ( " loadUrl( " + url + " ) " ) ;
this . url = url ;
int i = url . lastIndexOf ( '/' ) ;
if ( i > 0 ) {
this . baseUrl = url . substring ( 0 , i ) ;
}
else {
this . baseUrl = this . url ;
}
System . out . println ( " url= " + url + " baseUrl= " + baseUrl ) ;
// Init web view if not already done
if ( this . appView = = null ) {
this . init ( ) ;
}
// Initialize callback server
this . callbackServer . init ( url ) ;
2010-11-13 12:38:27 +08:00
2010-11-12 11:56:56 +08:00
// If loadingDialog, then show the App loading dialog
2010-11-12 12:24:20 +08:00
String loading = this . getProperty ( " loadingDialog " , 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 ;
}
}
JSONArray parm = new JSONArray ( ) ;
parm . put ( title ) ;
parm . put ( message ) ;
this . pluginManager . exec ( " Notification " , " activityStart " , null , parm . toString ( ) , false ) ;
2010-11-12 11:56:56 +08:00
}
final DroidGap me = this ;
2010-11-13 12:38:27 +08:00
// Create a timeout timer for loadUrl
Runnable runnable = new Runnable ( ) {
public void run ( ) {
me . loadUrlTimeout = true ;
try {
synchronized ( this ) {
wait ( me . loadUrlTimeoutValue ) ;
}
} catch ( InterruptedException e ) {
e . printStackTrace ( ) ;
}
// If timeout, then stop loading and handle error
if ( me . loadUrlTimeout ) {
me . appView . stopLoading ( ) ;
me . onReceivedError ( - 6 , " The connection to the server was unsuccessful. " , url ) ;
}
}
} ;
Thread thread = new Thread ( runnable ) ;
thread . start ( ) ;
// Load URL on UI thread
2010-11-12 04:00:56 +08:00
this . runOnUiThread ( new Runnable ( ) {
public void run ( ) {
2010-11-12 11:56:56 +08:00
me . appView . loadUrl ( url ) ;
2010-11-12 04:00:56 +08:00
}
} ) ;
2010-11-12 01:34:12 +08:00
}
2010-07-08 06:18:14 +08:00
2010-11-12 11:56:56 +08:00
/ * *
* 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 , final int time ) {
System . out . println ( " loadUrl( " + url + " , " + time + " ) " ) ;
final DroidGap me = this ;
Runnable runnable = new Runnable ( ) {
public void run ( ) {
try {
synchronized ( this ) {
this . wait ( time ) ;
}
} catch ( InterruptedException e ) {
e . printStackTrace ( ) ;
}
me . loadUrl ( url ) ;
}
} ;
Thread thread = new Thread ( runnable ) ;
thread . start ( ) ;
}
2010-11-13 12:38:27 +08:00
/ * *
* Load the url into the webview .
*
* @param url
* /
public void loadSplashScreen ( final String url ) {
System . out . println ( " loadSplashScreen( " + url + " ) " ) ;
2010-11-16 06:32:55 +08:00
this . splashScreenShowing = url ;
2010-11-13 12:38:27 +08:00
// Load URL on UI thread
final DroidGap me = this ;
Runnable runnable = new Runnable ( ) {
public void run ( ) {
me . runOnUiThread ( new Runnable ( ) {
public void run ( ) {
me . appView . loadUrl ( url ) ;
}
} ) ;
}
} ;
Thread thread = new Thread ( runnable ) ;
thread . start ( ) ;
}
2010-11-12 01:34:12 +08:00
@Override
2010-08-12 23:51:12 +08:00
/ * *
* Called by the system when the device configuration changes while your activity is running .
*
* @param Configuration newConfig
* /
2009-04-02 07:56:43 +08:00
public void onConfigurationChanged ( Configuration newConfig ) {
2010-11-12 04:00:56 +08:00
//don't reload the current page when the orientation is changed
super . onConfigurationChanged ( newConfig ) ;
2010-11-12 01:34:12 +08:00
}
/ * *
* Get boolean property for activity .
*
* @param name
* @param defaultValue
* @return
* /
protected boolean getProperty ( String name , boolean defaultValue ) {
Bundle bundle = this . getIntent ( ) . getExtras ( ) ;
if ( bundle = = null ) {
return defaultValue ;
}
Boolean p = ( Boolean ) bundle . get ( name ) ;
if ( p = = null ) {
return defaultValue ;
}
return p . booleanValue ( ) ;
}
/ * *
* Get int property for activity .
*
* @param name
* @param defaultValue
* @return
* /
protected int getProperty ( String name , int defaultValue ) {
Bundle bundle = this . getIntent ( ) . getExtras ( ) ;
if ( bundle = = null ) {
return defaultValue ;
}
Integer p = ( Integer ) bundle . get ( name ) ;
if ( p = = null ) {
return defaultValue ;
}
return p . intValue ( ) ;
}
/ * *
* Get string property for activity .
*
* @param name
* @param defaultValue
* @return
* /
protected String getProperty ( String name , String defaultValue ) {
Bundle bundle = this . getIntent ( ) . getExtras ( ) ;
if ( bundle = = null ) {
return defaultValue ;
}
String p = bundle . getString ( name ) ;
if ( p = = null ) {
return defaultValue ;
}
return p ;
}
/ * *
* Get double property for activity .
*
* @param name
* @param defaultValue
* @return
* /
protected double getProperty ( String name , double defaultValue ) {
Bundle bundle = this . getIntent ( ) . getExtras ( ) ;
if ( bundle = = null ) {
return defaultValue ;
}
Double p = ( Double ) bundle . get ( name ) ;
if ( p = = null ) {
return defaultValue ;
}
return p . doubleValue ( ) ;
}
/ * *
* Set boolean property on activity .
*
* @param name
* @param value
* /
protected void setProperty ( String name , boolean value ) {
if ( this . appView ! = null ) {
System . out . println ( " Setting property after webview is already initialized - no effect. " ) ;
}
this . getIntent ( ) . putExtra ( name , value ) ;
}
/ * *
* Set int property on activity .
*
* @param name
* @param value
* /
protected void setProperty ( String name , int value ) {
if ( this . appView ! = null ) {
System . out . println ( " Setting property after webview is already initialized - no effect. " ) ;
}
this . getIntent ( ) . putExtra ( name , value ) ;
}
/ * *
* Set string property on activity .
*
* @param name
* @param value
* /
protected void setProperty ( String name , String value ) {
if ( this . appView ! = null ) {
System . out . println ( " Setting property after webview is already initialized - no effect. " ) ;
}
this . getIntent ( ) . putExtra ( name , value ) ;
}
/ * *
* Set double property on activity .
*
* @param name
* @param value
* /
protected void setProperty ( String name , double value ) {
if ( this . appView ! = null ) {
System . out . println ( " Setting property after webview is already initialized - no effect. " ) ;
}
this . getIntent ( ) . putExtra ( name , value ) ;
}
2010-08-12 23:51:12 +08:00
@Override
/ * *
* Called when the system is about to start resuming a previous activity .
* /
2010-09-08 02:59:54 +08:00
protected void onPause ( ) {
2010-08-12 23:51:12 +08:00
super . onPause ( ) ;
2010-09-08 02:59:54 +08:00
2010-11-15 07:33:06 +08:00
// If app doesn't want to run in background
if ( ! this . keepRunning ) {
// Forward to plugins
this . pluginManager . onPause ( ) ;
// Send pause event to JavaScript
this . appView . loadUrl ( " javascript:try{PhoneGap.onPause.fire();}catch(e){}; " ) ;
// Pause JavaScript timers (including setInterval)
this . appView . pauseTimers ( ) ;
}
2010-08-12 23:51:12 +08:00
}
@Override
/ * *
* Called when the activity will start interacting with the user .
* /
2010-09-08 02:59:54 +08:00
protected void onResume ( ) {
2010-08-12 23:51:12 +08:00
super . onResume ( ) ;
2010-08-20 04:41:06 +08:00
2010-11-15 07:33:06 +08:00
// If app doesn't want to run in background
if ( ! this . keepRunning ) {
// Forward to plugins
this . pluginManager . onResume ( ) ;
// Send resume event to JavaScript
this . appView . loadUrl ( " javascript:try{PhoneGap.onResume.fire();}catch(e){}; " ) ;
// Resume JavaScript timers (including setInterval)
this . appView . resumeTimers ( ) ;
}
2010-08-12 23:51:12 +08:00
}
2009-04-02 07:56:43 +08:00
2010-08-12 23:51:12 +08:00
@Override
/ * *
* The final call you receive before your activity is destroyed .
* /
public void onDestroy ( ) {
super . onDestroy ( ) ;
2010-08-26 23:39:02 +08:00
// Make sure pause event is sent if onPause hasn't been called before onDestroy
2010-09-10 00:01:56 +08:00
this . appView . loadUrl ( " javascript:try{PhoneGap.onPause.fire();}catch(e){}; " ) ;
2010-08-26 23:39:02 +08:00
2010-08-12 23:51:12 +08:00
// Load blank page so that JavaScript onunload is called
2010-09-10 00:01:56 +08:00
this . appView . loadUrl ( " about:blank " ) ;
2010-08-12 23:51:12 +08:00
// Clean up objects
2010-09-10 00:01:56 +08:00
if ( this . mKey ! = null ) {
2010-08-19 02:12:53 +08:00
}
2010-09-04 03:14:28 +08:00
2010-09-08 02:59:54 +08:00
// Forward to plugins
this . pluginManager . onDestroy ( ) ;
2010-09-07 02:13:09 +08:00
2010-09-10 00:01:56 +08:00
if ( this . callbackServer ! = null ) {
this . callbackServer . destroy ( ) ;
2010-08-19 02:12:53 +08:00
}
2010-08-12 23:51:12 +08:00
}
2010-09-11 00:31:22 +08:00
/ * *
* Add a class that implements a service .
*
* @param serviceType
* @param className
* /
public void addService ( String serviceType , String className ) {
this . pluginManager . addService ( serviceType , className ) ;
}
2010-08-19 02:12:53 +08:00
/ * *
* Send JavaScript statement back to JavaScript .
2010-11-12 01:34:12 +08:00
* ( This is a convenience method )
2010-08-19 02:12:53 +08:00
*
* @param message
* /
public void sendJavascript ( String statement ) {
this . callbackServer . sendJavascript ( statement ) ;
}
2010-09-07 23:33:08 +08:00
/ * *
* Provides a hook for calling " alert " from javascript . Useful for
* debugging your javascript .
* /
public class GapClient extends WebChromeClient {
2010-09-10 00:01:56 +08:00
private Context ctx ;
/ * *
* Constructor .
*
* @param ctx
* /
2010-09-07 23:33:08 +08:00
public GapClient ( Context ctx ) {
2010-09-10 00:01:56 +08:00
this . ctx = ctx ;
2010-09-07 23:33:08 +08:00
}
2010-09-10 00:01:56 +08:00
2010-09-07 23:33:08 +08:00
/ * *
* Tell the client to display a javascript alert dialog .
*
* @param view
* @param url
* @param message
* @param result
* /
@Override
public boolean onJsAlert ( WebView view , String url , String message , final JsResult result ) {
2010-09-10 00:01:56 +08:00
AlertDialog . Builder dlg = new AlertDialog . Builder ( this . ctx ) ;
2010-09-07 23:33:08 +08:00
dlg . setMessage ( message ) ;
dlg . setTitle ( " Alert " ) ;
dlg . setCancelable ( false ) ;
dlg . setPositiveButton ( android . R . string . ok ,
new AlertDialog . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
result . confirm ( ) ;
}
} ) ;
dlg . create ( ) ;
dlg . show ( ) ;
return true ;
}
2009-12-17 03:09:32 +08:00
2010-09-07 23:33:08 +08:00
/ * *
* Tell the client to display a confirm dialog to the user .
*
* @param view
* @param url
* @param message
* @param result
* /
@Override
public boolean onJsConfirm ( WebView view , String url , String message , final JsResult result ) {
2010-09-10 00:01:56 +08:00
AlertDialog . Builder dlg = new AlertDialog . Builder ( this . ctx ) ;
2010-09-07 23:33:08 +08:00
dlg . setMessage ( message ) ;
dlg . setTitle ( " Confirm " ) ;
dlg . setCancelable ( false ) ;
dlg . setPositiveButton ( android . R . string . ok ,
new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
result . confirm ( ) ;
}
} ) ;
dlg . setNegativeButton ( android . R . string . cancel ,
new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
result . cancel ( ) ;
}
} ) ;
dlg . create ( ) ;
dlg . show ( ) ;
return true ;
}
2009-12-01 07:41:24 +08:00
2010-09-07 23:33:08 +08:00
}
2010-09-10 00:01:56 +08:00
/ * *
* WebChromeClient that extends GapClient with additional support for Android 2 . X
* /
public final class EclairClient extends GapClient {
2010-11-12 04:00:56 +08:00
private String TAG = " PhoneGapLog " ;
private long MAX_QUOTA = 100 * 1024 * 1024 ;
/ * *
* Constructor .
*
* @param ctx
* /
public EclairClient ( Context ctx ) {
super ( ctx ) ;
}
/ * *
* Handle database quota exceeded notification .
*
* @param url
* @param databaseIdentifier
* @param currentQuota
* @param estimatedSize
* @param totalUsedQuota
* @param quotaUpdater
* /
@Override
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 ) ) ;
if ( estimatedSize < MAX_QUOTA )
{
//increase for 1Mb
long newQuota = estimatedSize ;
Log . d ( TAG , " calling quotaUpdater.updateQuota newQuota: " + Long . toString ( newQuota ) ) ;
quotaUpdater . updateQuota ( newQuota ) ;
}
else
{
// Set the quota to whatever it is and force an error
// TODO: get docs on how to handle this properly
quotaUpdater . updateQuota ( currentQuota ) ;
}
}
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
@Override
public void onConsoleMessage ( String message , int lineNumber , String sourceID )
{
// This is a kludgy hack!!!!
Log . d ( TAG , sourceID + " : Line " + Integer . toString ( lineNumber ) + " : " + message ) ;
}
@Override
public void onGeolocationPermissionsShowPrompt ( String origin , Callback callback ) {
// TODO Auto-generated method stub
super . onGeolocationPermissionsShowPrompt ( origin , callback ) ;
callback . invoke ( origin , true , false ) ;
}
}
2010-08-27 22:31:57 +08:00
/ * *
* The webview client receives notifications about appView
* /
2010-08-25 02:19:22 +08:00
public class GapViewClient extends WebViewClient {
2010-08-27 03:15:34 +08:00
2010-09-10 00:01:56 +08:00
DroidGap ctx ;
2010-08-25 02:19:22 +08:00
2010-08-26 23:39:02 +08:00
/ * *
* Constructor .
*
* @param ctx
* /
public GapViewClient ( DroidGap ctx ) {
2010-09-10 00:01:56 +08:00
this . ctx = ctx ;
2010-08-25 02:19:22 +08:00
}
2010-08-26 23:39:02 +08:00
/ * *
* Give the host application a chance to take over the control when a new url
* is about to be loaded in the current WebView .
*
* @param view The WebView that is initiating the callback .
* @param url The url to be loaded .
* @return true to override , false for default behavior
* /
@Override
public boolean shouldOverrideUrlLoading ( WebView view , String url ) {
// If dialing phone (tel:5551212)
if ( url . startsWith ( WebView . SCHEME_TEL ) ) {
try {
Intent intent = new Intent ( Intent . ACTION_DIAL ) ;
intent . setData ( Uri . parse ( url ) ) ;
startActivity ( intent ) ;
} catch ( android . content . ActivityNotFoundException e ) {
System . out . println ( " Error dialing " + url + " : " + e . toString ( ) ) ;
}
return true ;
}
// If displaying map (geo:0,0?q=address)
2010-10-28 23:18:28 +08:00
else if ( url . startsWith ( " geo: " ) ) {
2010-08-26 23:39:02 +08:00
try {
Intent intent = new Intent ( Intent . ACTION_VIEW ) ;
intent . setData ( Uri . parse ( url ) ) ;
startActivity ( intent ) ;
} catch ( android . content . ActivityNotFoundException e ) {
System . out . println ( " Error showing map " + url + " : " + e . toString ( ) ) ;
}
return true ;
}
// If sending email (mailto:abc@corp.com)
else if ( url . startsWith ( WebView . SCHEME_MAILTO ) ) {
try {
Intent intent = new Intent ( Intent . ACTION_VIEW ) ;
intent . setData ( Uri . parse ( url ) ) ;
startActivity ( intent ) ;
} catch ( android . content . ActivityNotFoundException e ) {
System . out . println ( " Error sending email " + url + " : " + e . toString ( ) ) ;
}
return true ;
}
// If sms:5551212
else if ( url . startsWith ( " sms: " ) ) {
try {
Intent intent = new Intent ( Intent . ACTION_VIEW ) ;
intent . setData ( Uri . parse ( url ) ) ;
intent . putExtra ( " address " , url . substring ( 4 ) ) ;
intent . setType ( " vnd.android-dir/mms-sms " ) ;
startActivity ( intent ) ;
} catch ( android . content . ActivityNotFoundException e ) {
System . out . println ( " Error sending sms " + url + " : " + e . toString ( ) ) ;
}
return true ;
}
// If http, https or file
else if ( url . startsWith ( " http:// " ) | | url . startsWith ( " https:// " ) | | url . startsWith ( " file:// " ) ) {
int i = url . lastIndexOf ( '/' ) ;
String newBaseUrl = url ;
if ( i > 0 ) {
newBaseUrl = url . substring ( 0 , i ) ;
}
// If our app or file:, then load into our webview
2010-09-10 00:01:56 +08:00
if ( this . ctx . loadInWebView | | url . startsWith ( " file:// " ) | | this . ctx . baseUrl . equals ( newBaseUrl ) ) {
this . ctx . appView . loadUrl ( url ) ;
2010-08-26 23:39:02 +08:00
}
// If not our application, let default viewer handle
else {
try {
Intent intent = new Intent ( Intent . ACTION_VIEW ) ;
intent . setData ( Uri . parse ( url ) ) ;
startActivity ( intent ) ;
} catch ( android . content . ActivityNotFoundException e ) {
System . out . println ( " Error loading url " + url + " : " + e . toString ( ) ) ;
}
}
return true ;
}
return false ;
}
/ * *
* Notify the host application that a page has finished loading .
*
* @param view The webview initiating the callback .
* @param url The url of the page .
* /
@Override
2010-11-13 12:38:27 +08:00
public void onPageFinished ( WebView view , String url ) {
2010-08-26 23:39:02 +08:00
super . onPageFinished ( view , url ) ;
2010-11-13 12:38:27 +08:00
// Clear timeout flag
this . ctx . loadUrlTimeout = false ;
// 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;} " ) ;
2010-11-12 05:59:35 +08:00
// If splash screen is showing, clear it
if ( this . ctx . splashscreen ! = 0 ) {
this . ctx . splashscreen = 0 ;
appView . setBackgroundResource ( 0 ) ;
}
2010-11-13 12:38:27 +08:00
// Stop "app loading" spinner if showing
if ( this . ctx . hideLoadingDialogOnPageLoad ) {
this . ctx . hideLoadingDialogOnPageLoad = false ;
this . ctx . pluginManager . exec ( " Notification " , " activityStop " , null , " [] " , false ) ;
}
2010-11-16 06:32:55 +08:00
// Clear history, so that splash screen isn't there when Back button is pressed
WebBackForwardList history = this . ctx . appView . copyBackForwardList ( ) ;
int i = history . getCurrentIndex ( ) ;
if ( i > 0 ) {
WebHistoryItem item = history . getItemAtIndex ( i - 1 ) ;
if ( item . getUrl ( ) . equals ( this . ctx . splashScreenShowing ) ) {
this . ctx . appView . clearHistory ( ) ;
}
}
2010-08-25 02:19:22 +08:00
}
2010-11-13 12:38:27 +08:00
/ * *
* 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 view The WebView that is initiating the callback .
* @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 .
* /
@Override
public void onReceivedError ( WebView view , int errorCode , String description , String failingUrl ) {
System . out . println ( " onReceivedError: Error code= " + errorCode + " Description= " + description + " URL= " + failingUrl ) ;
2010-08-25 02:19:22 +08:00
2010-11-13 12:38:27 +08:00
// Clear timeout flag
this . ctx . loadUrlTimeout = false ;
// Handle error
this . ctx . onReceivedError ( errorCode , description , failingUrl ) ;
}
}
2010-11-12 04:08:55 +08:00
/ * *
* Called when a key is pressed .
*
* @param keyCode
* @param event
* /
@Override
public boolean onKeyDown ( int keyCode , KeyEvent event ) {
// If back key
if ( keyCode = = KeyEvent . KEYCODE_BACK ) {
// If back key is bound, then send event to JavaScript
if ( mKey . isBound ( ) ) {
this . appView . loadUrl ( " javascript:document.keyEvent.backTrigger() " ) ;
}
// If not bound
else {
// Go to previous page in webview if it is possible to go back
if ( this . appView . canGoBack ( ) ) {
this . appView . goBack ( ) ;
}
// If not, then invoke behavior of super class
else {
return super . onKeyDown ( keyCode , event ) ;
}
}
}
2010-11-12 06:20:32 +08:00
// If menu key
else if ( keyCode = = KeyEvent . KEYCODE_MENU ) {
2010-11-12 04:08:55 +08:00
appView . loadUrl ( " javascript:keyEvent.menuTrigger() " ) ;
}
2010-11-12 06:20:32 +08:00
// If search key
else if ( keyCode = = KeyEvent . KEYCODE_SEARCH ) {
appView . loadUrl ( " javascript:keyEvent.searchTrigger() " ) ;
}
2010-11-12 04:08:55 +08:00
return false ;
2009-12-17 04:50:08 +08:00
}
2010-11-12 04:08:55 +08:00
2010-09-04 03:01:24 +08:00
/ * *
2010-09-07 02:13:09 +08:00
* Any calls to Activity . startActivityForResult must use method below , so
2010-09-04 03:01:24 +08:00
* the result can be routed to them correctly .
*
* This is done to eliminate the need to modify DroidGap . java to receive activity results .
*
* @param intent The intent to start
* @param requestCode Identifies who to send the result to
*
* @throws RuntimeException
* /
@Override
public void startActivityForResult ( Intent intent , int requestCode ) throws RuntimeException {
2010-09-07 02:13:09 +08:00
System . out . println ( " startActivityForResult(intent, " + requestCode + " ) " ) ;
if ( requestCode = = - 1 ) {
super . startActivityForResult ( intent , requestCode ) ;
2010-09-04 03:01:24 +08:00
}
else {
2010-09-07 02:13:09 +08:00
throw new RuntimeException ( " PhoneGap Exception: Call startActivityForResult(Command, Intent) instead. " ) ;
2010-09-04 03:01:24 +08:00
}
}
2010-09-07 02:13:09 +08:00
2010-09-04 03:01:24 +08:00
/ * *
2010-09-07 02:13:09 +08:00
* 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
2010-09-17 00:04:27 +08:00
* @param requestCode The request code that is passed to callback to identify the activity
2010-09-04 03:01:24 +08:00
* /
2010-09-17 00:04:27 +08:00
public void startActivityForResult ( Plugin command , Intent intent , int requestCode ) {
this . activityResultCallback = command ;
2010-09-07 02:13:09 +08:00
super . startActivityForResult ( intent , requestCode ) ;
2010-09-04 03:01:24 +08:00
}
2010-09-07 02:13:09 +08:00
@Override
2010-09-03 00:27:48 +08:00
/ * *
* 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 " ) .
* /
2010-11-13 12:38:27 +08:00
protected void onActivityResult ( int requestCode , int resultCode , Intent intent ) {
super . onActivityResult ( requestCode , resultCode , intent ) ;
Plugin callback = this . activityResultCallback ;
if ( callback ! = null ) {
callback . onActivityResult ( requestCode , resultCode , intent ) ;
}
}
/ * *
* 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 ( int errorCode , String description , String failingUrl ) {
final DroidGap me = this ;
// Stop "app loading" spinner if showing
me . pluginManager . exec ( " Notification " , " activityStop " , null , " [] " , false ) ;
// If errorUrl specified, then load it
final String errorUrl = me . getProperty ( " errorUrl " , null ) ;
if ( ( errorUrl ! = null ) & & errorUrl . startsWith ( " file:// " ) & & ( ! failingUrl . equals ( errorUrl ) ) ) {
// Load URL on UI thread
me . runOnUiThread ( new Runnable ( ) {
public void run ( ) {
me . appView . loadUrl ( errorUrl ) ;
}
} ) ;
}
// If not, then display error dialog
else {
me . appView . loadUrl ( " about:blank " ) ;
me . displayError ( " Application Error " , description + " ( " + failingUrl + " ) " , " OK " , true ) ;
}
}
/ * *
* 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 DroidGap me = this ;
me . runOnUiThread ( new Runnable ( ) {
public void run ( ) {
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 ) {
me . finish ( ) ;
}
}
} ) ;
dlg . create ( ) ;
dlg . show ( ) ;
}
} ) ;
}
2009-09-12 05:30:24 +08:00
}