Use native Android API for user-location (WebView causes high cpu load, even when IITC is stopped)

Also, move all user-location related functions into new class IITC_UserLocation.
This commit is contained in:
fkloft 2013-12-17 21:23:47 +01:00
parent f1aaad3c22
commit be05945eac
5 changed files with 257 additions and 130 deletions

View File

@ -59,9 +59,6 @@ window.plugin.userLocation.setup = function() {
window.plugin.userLocation.circle = circle; window.plugin.userLocation.circle = circle;
window.plugin.userLocation.icon = icon; window.plugin.userLocation.icon = icon;
if('ondeviceorientation' in window)
window.addEventListener('deviceorientation', window.plugin.userLocation.onDeviceOrientation, false);
window.map.on('zoomend', window.plugin.userLocation.onZoomEnd); window.map.on('zoomend', window.plugin.userLocation.onZoomEnd);
window.plugin.userLocation.onZoomEnd(); window.plugin.userLocation.onZoomEnd();
}; };
@ -73,20 +70,25 @@ window.plugin.userLocation.onZoomEnd = function() {
window.plugin.userLocation.locationLayer.addLayer(window.plugin.userLocation.circle); window.plugin.userLocation.locationLayer.addLayer(window.plugin.userLocation.circle);
}; };
window.plugin.userLocation.onDeviceOrientation = function(e) { window.plugin.userLocation.locate = function(lat, lng, accuracy) {
var direction, delta, heading; var latlng = new L.LatLng(lat, lng);
if (typeof e.webkitCompassHeading !== 'undefined') { var latAccuracy = 180 * accuracy / 40075017;
direction = e.webkitCompassHeading; var lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat);
if (typeof window.orientation !== 'undefined') {
direction += window.orientation;
}
}
else {
// http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation
direction = 360 - e.alpha;
}
var zoom = window.map.getBoundsZoom(L.latLngBounds(
[lat - latAccuracy, lng - lngAccuracy],
[lat + latAccuracy, lng + lngAccuracy]));
window.map.setView(latlng, zoom);
}
window.plugin.userLocation.onLocationChange = function(lat, lng) {
var latlng = new L.LatLng(lat, lng);
window.plugin.userLocation.marker.setLatLng(latlng);
window.plugin.userLocation.circle.setLatLng(latlng);
};
window.plugin.userLocation.onOrientationChange = function(direction) {
$(".container", window.plugin.userLocation.marker._icon) $(".container", window.plugin.userLocation.marker._icon)
.removeClass("circle") .removeClass("circle")
.addClass("arrow") .addClass("arrow")
@ -96,12 +98,6 @@ window.plugin.userLocation.onDeviceOrientation = function(e) {
}); });
} }
window.plugin.userLocation.updateLocation = function(lat, lng) {
var latlng = new L.LatLng(lat, lng);
window.plugin.userLocation.marker.setLatLng(latlng);
window.plugin.userLocation.circle.setLatLng(latlng);
};
var setup = window.plugin.userLocation.setup; var setup = window.plugin.userLocation.setup;
// PLUGIN END ////////////////////////////////////////////////////////// // PLUGIN END //////////////////////////////////////////////////////////

View File

@ -14,8 +14,6 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.location.Location; import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -37,24 +35,28 @@ import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Stack; import java.util.Stack;
public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeListener, LocationListener { public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeListener {
private static final int REQUEST_LOGIN = 1; private static final int REQUEST_LOGIN = 1;
private static final String mIntelUrl = "https://www.ingress.com/intel";
private IITC_WebView mIitcWebView;
private final String mIntelUrl = "https://www.ingress.com/intel";
private boolean mIsLocEnabled = false;
private Location mLastLocation = null;
private LocationManager mLocMngr = null;
private IITC_DeviceAccountLogin mLogin;
private MenuItem mSearchMenuItem;
private boolean mDesktopMode = false;
private boolean mAdvancedMenu = false;
private boolean mReloadNeeded = false;
private final Stack<String> mDialogStack = new Stack<String>();
private SharedPreferences mSharedPrefs; private SharedPreferences mSharedPrefs;
private IITC_WebView mIitcWebView;
private IITC_UserLocation mUserLocation;
private IITC_NavigationHelper mNavigationHelper; private IITC_NavigationHelper mNavigationHelper;
private IITC_MapSettings mMapSettings; private IITC_MapSettings mMapSettings;
private IITC_DeviceAccountLogin mLogin;
private boolean mDesktopMode = false;
private boolean mAdvancedMenu = false;
private MenuItem mSearchMenuItem;
private boolean mReloadNeeded = false;
private boolean mIsLoading = true;
private final Stack<String> mDialogStack = new Stack<String>();
// Used for custom back stack handling
private final Stack<Pane> mBackStack = new Stack<IITC_NavigationHelper.Pane>();
private Pane mCurrentPane = Pane.MAP;
private boolean mBackButtonPressed = false;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override @Override
@ -63,11 +65,6 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
} }
}; };
// Used for custom back stack handling
private final Stack<Pane> mBackStack = new Stack<IITC_NavigationHelper.Pane>();
private Pane mCurrentPane = Pane.MAP;
private boolean mBackButtonPressed = false;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -91,26 +88,14 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
// get fullscreen status from settings // get fullscreen status from settings
mIitcWebView.updateFullscreenStatus(); mIitcWebView.updateFullscreenStatus();
// Acquire a reference to the system Location Manager mUserLocation = new IITC_UserLocation(this);
mLocMngr = (LocationManager) this mUserLocation.setEnabled(mSharedPrefs.getBoolean("pref_user_loc", false));
.getSystemService(Context.LOCATION_SERVICE);
mIsLocEnabled = mSharedPrefs.getBoolean("pref_user_loc", false);
if (mIsLocEnabled) {
// Register the mSharedPrefChangeListener with the Location Manager to receive
// location updates
mLocMngr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
0, 0, this);
mLocMngr.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
this);
}
// pass ActionBar to helper because we deprecated getActionBar // pass ActionBar to helper because we deprecated getActionBar
mNavigationHelper = new IITC_NavigationHelper(this, super.getActionBar()); mNavigationHelper = new IITC_NavigationHelper(this, super.getActionBar());
mMapSettings = new IITC_MapSettings(this); mMapSettings = new IITC_MapSettings(this);
// Clear the back stack // Clear the back stack
mBackStack.clear(); mBackStack.clear();
@ -121,14 +106,13 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
handleIntent(getIntent(), true); handleIntent(getIntent(), true);
} }
// --------------------- onSharedPreferenceListener -----------------------
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("pref_force_desktop")) { if (key.equals("pref_force_desktop")) {
mDesktopMode = sharedPreferences.getBoolean("pref_force_desktop", false); mDesktopMode = sharedPreferences.getBoolean("pref_force_desktop", false);
mNavigationHelper.onPrefChanged(); mNavigationHelper.onPrefChanged();
} else if (key.equals("pref_user_loc")) { } else if (key.equals("pref_user_loc")) {
mIsLocEnabled = sharedPreferences.getBoolean("pref_user_loc", false); mUserLocation.setEnabled(sharedPreferences.getBoolean("pref_user_loc", false));
} else if (key.equals("pref_fullscreen")) { } else if (key.equals("pref_fullscreen")) {
mIitcWebView.updateFullscreenStatus(); mIitcWebView.updateFullscreenStatus();
mNavigationHelper.onPrefChanged(); mNavigationHelper.onPrefChanged();
@ -154,33 +138,6 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
mReloadNeeded = true; mReloadNeeded = true;
} }
// ------------------------------------------------------------------------
// ------------------------ LocationListener ------------------------------
@Override
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location
// provider.
drawMarker(location);
mLastLocation = location;
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
// ------------------------------------------------------------------------
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
@ -287,19 +244,14 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
} }
@Override @Override
protected void onResume() { protected void onStart() {
super.onResume(); super.onStart();
// enough idle...let's do some work // enough idle...let's do some work
Log.d("iitcm", "resuming...reset idleTimer"); Log.d("iitcm", "resuming...reset idleTimer");
mIitcWebView.updateCaching(false); mIitcWebView.updateCaching(false);
if (mIsLocEnabled) { mUserLocation.onStart();
// Register the mSharedPrefChangeListener with the Location Manager to receive
// location updates
mLocMngr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
mLocMngr.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
if (mReloadNeeded) { if (mReloadNeeded) {
Log.d("iitcm", "preference had changed...reload needed"); Log.d("iitcm", "preference had changed...reload needed");
@ -317,9 +269,7 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
Log.d("iitcm", "stopping iitcm"); Log.d("iitcm", "stopping iitcm");
mIitcWebView.loadUrl("javascript: window.idleSet();"); mIitcWebView.loadUrl("javascript: window.idleSet();");
if (mIsLocEnabled) { mUserLocation.onStop();
mLocMngr.removeUpdates(this);
}
super.onStop(); super.onStop();
} }
@ -481,17 +431,13 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
return true; return true;
case R.id.locate: // get the users current location and focus it on map case R.id.locate: // get the users current location and focus it on map
switchToPane(Pane.MAP); switchToPane(Pane.MAP);
// get location from network by default
if (!mIsLocEnabled) { if (mUserLocation.hasCurrentLocation()) {
mIitcWebView.loadUrl("javascript: " +
"window.map.locate({setView : true, maxZoom: 15});");
// if gps location is displayed we can use a better location without any costs // if gps location is displayed we can use a better location without any costs
mUserLocation.locate();
} else { } else {
if (mLastLocation != null) { // get location from network by default
mIitcWebView.loadUrl("javascript: window.map.setView(new L.LatLng(" + mIitcWebView.loadUrl("javascript: window.map.locate({setView : true});");
mLastLocation.getLatitude() + "," +
mLastLocation.getLongitude() + "), 15);");
}
} }
return true; return true;
case R.id.action_settings: // start settings activity case R.id.action_settings: // start settings activity
@ -552,20 +498,6 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
mIitcWebView.loadUrl(url); mIitcWebView.loadUrl(url);
} }
// update the user location marker on the map
public void drawMarker(Location loc) {
// throw away all positions with accuracy > 100 meters
// should avoid gps glitches
if (loc.getAccuracy() < 100) {
// do not touch the javascript while iitc boots
if (findViewById(R.id.imageLoading).getVisibility() == View.GONE) {
mIitcWebView.loadUrl("javascript: "
+ "window.plugin.userLocation.updateLocation( "
+ loc.getLatitude() + ", " + loc.getLongitude() + ");");
}
}
}
public IITC_WebView getWebView() { public IITC_WebView getWebView() {
return this.mIitcWebView; return this.mIitcWebView;
} }
@ -594,7 +526,7 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
* called by IITC_WebViewClient when the Google login form is opened. * called by IITC_WebViewClient when the Google login form is opened.
*/ */
public void onReceivedLoginRequest(IITC_WebViewClient client, WebView view, public void onReceivedLoginRequest(IITC_WebViewClient client, WebView view,
String realm, String account, String args) { String realm, String account, String args) {
mLogin = new IITC_DeviceAccountLogin(this, view, client); mLogin = new IITC_DeviceAccountLogin(this, view, client);
mLogin.startLogin(realm, account, args); mLogin.startLogin(realm, account, args);
} }
@ -629,7 +561,9 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
} }
public void setLoadingState(boolean isLoading) { public void setLoadingState(boolean isLoading) {
mNavigationHelper.setLoadingState(isLoading); mIsLoading = isLoading;
mNavigationHelper.onLoadingStateChanged();
if (isLoading && !mSharedPrefs.getBoolean("pref_disable_splash", false)) { if (isLoading && !mSharedPrefs.getBoolean("pref_disable_splash", false)) {
findViewById(R.id.iitc_webview).setVisibility(View.GONE); findViewById(R.id.iitc_webview).setVisibility(View.GONE);
@ -668,6 +602,10 @@ public class IITC_Mobile extends Activity implements OnSharedPreferenceChangeLis
finish(); finish();
} }
public boolean isLoading() {
return mIsLoading;
}
/** /**
* @see getNavigationHelper() * @see getNavigationHelper()
* @deprecated ActionBar related stuff should be handled by IITC_NavigationHelper * @deprecated ActionBar related stuff should be handled by IITC_NavigationHelper

View File

@ -51,7 +51,6 @@ public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnIt
private final View mDrawerRight; private final View mDrawerRight;
private boolean mDesktopMode = false; private boolean mDesktopMode = false;
private boolean mIsLoading;
private Pane mPane = Pane.MAP; private Pane mPane = Pane.MAP;
private String mHighlighter = null; private String mHighlighter = null;
private int mDialogs = 0; private int mDialogs = 0;
@ -150,7 +149,7 @@ public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnIt
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight); mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight);
setDrawerIndicatorEnabled(false); setDrawerIndicatorEnabled(false);
} else { } else {
if (mIsLoading) { if (mIitc.isLoading()) {
mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator
mActionBar.setHomeButtonEnabled(false);// Make icon unclickable mActionBar.setHomeButtonEnabled(false);// Make icon unclickable
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
@ -175,7 +174,7 @@ public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnIt
} }
boolean mapVisible = mDesktopMode || mPane == Pane.MAP; boolean mapVisible = mDesktopMode || mPane == Pane.MAP;
if ("No Highlights".equals(mHighlighter) || isDrawerOpened() || mIsLoading || !mapVisible) { if ("No Highlights".equals(mHighlighter) || isDrawerOpened() || mIitc.isLoading() || !mapVisible) {
mActionBar.setSubtitle(null); mActionBar.setSubtitle(null);
} else { } else {
mActionBar.setSubtitle(mHighlighter); mActionBar.setSubtitle(mHighlighter);
@ -255,6 +254,10 @@ public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnIt
mDrawerLayout.closeDrawer(mDrawerLeft); mDrawerLayout.closeDrawer(mDrawerLeft);
} }
public void onLoadingStateChanged() {
updateViews();
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
@ -295,11 +298,6 @@ public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnIt
updateViews(); updateViews();
} }
public void setLoadingState(boolean isLoading) {
mIsLoading = isLoading;
updateViews();
}
public void showActionBar() { public void showActionBar() {
mActionBar.show(); mActionBar.show();
} }

View File

@ -0,0 +1,196 @@
package com.cradle.iitc_mobile;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.Surface;
public class IITC_UserLocation implements LocationListener, SensorEventListener {
private boolean mEnabled = false;
private IITC_Mobile mIitc;
private Location mLastLocation = null;
private LocationManager mLocationManager;
private boolean mRegistered = false;
private boolean mRunning = false;
private Sensor mSensorAccelerometer, mSensorMagnetometer;
private SensorManager mSensorManager = null;
float[] mValuesGravity = null, mValuesGeomagnetic = null;
public IITC_UserLocation(IITC_Mobile iitc) {
mIitc = iitc;
// Acquire a reference to the Location Manager and Sensor Manager
mLocationManager = (LocationManager) iitc.getSystemService(Context.LOCATION_SERVICE);
mSensorManager = (SensorManager) iitc.getSystemService(Context.SENSOR_SERVICE);
mSensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
private void registerListeners() {
if (mRegistered) return;
mRegistered = true;
try {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
} catch (IllegalArgumentException e) {
// if the given provider doesn't exist
e.printStackTrace();
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
} catch (IllegalArgumentException e) {
// if the given provider doesn't exist
e.printStackTrace();
}
if (mSensorAccelerometer != null && mSensorMagnetometer != null) {
mSensorManager.registerListener(this, mSensorAccelerometer, SensorManager.SENSOR_DELAY_UI);
mSensorManager.registerListener(this, mSensorMagnetometer, SensorManager.SENSOR_DELAY_UI);
}
}
private void unregisterListeners() {
if (!mRegistered) return;
mRegistered = false;
mLocationManager.removeUpdates(this);
if (mSensorAccelerometer != null && mSensorMagnetometer != null) {
mSensorManager.unregisterListener(this, mSensorAccelerometer);
mSensorManager.unregisterListener(this, mSensorMagnetometer);
}
}
private void updateListeners() {
if (mRunning && mEnabled)
registerListeners();
else
unregisterListeners();
}
public boolean hasCurrentLocation() {
if (!mRegistered) return false;
return mLastLocation != null;
}
public void locate() {
// do not touch the javascript while iitc boots
if (mIitc.isLoading()) return;
Location location = mLastLocation;
if (location == null) return;
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.locate("
+ location.getLatitude() + ", " + location.getLongitude() + ", " + location.getAccuracy() + ");");
}
public void onStart() {
mRunning = true;
updateListeners();
}
public void onStop() {
mRunning = false;
updateListeners();
}
public void setEnabled(boolean enabled) {
if (enabled == mEnabled) return;
mEnabled = enabled;
updateListeners();
}
// ------------------------------------------------------------------------
// <interface LocationListener>
@Override
public void onLocationChanged(Location location) {
mLastLocation = location;
// throw away all positions with accuracy > 100 meters should avoid gps glitches
if (location.getAccuracy() > 100) return;
// do not touch the javascript while iitc boots
if (mIitc.isLoading()) return;
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.onLocationChange("
+ location.getLatitude() + ", " + location.getLongitude() + ");");
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
// </interface LocationListener>
// ------------------------------------------------------------------------
// <interface SensorEventListener>
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
mValuesGravity = event.values;
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
mValuesGeomagnetic = event.values;
// do not touch the javascript while iitc boots
if (mIitc.isLoading()) return;
// wait until both sensors have given us an event
if (mValuesGravity == null || mValuesGeomagnetic == null) return;
float R[] = new float[9];
float I[] = new float[9];
float orientation[] = new float[3];
if (!SensorManager.getRotationMatrix(R, I, mValuesGravity, mValuesGeomagnetic)) return;
SensorManager.getOrientation(R, orientation);
double direction = orientation[0] / Math.PI * 180;
int rotation = mIitc.getWindowManager().getDefaultDisplay().getRotation();
switch (rotation) {
case Surface.ROTATION_90:
direction += 90;
break;
case Surface.ROTATION_180:
direction += 180;
break;
case Surface.ROTATION_270:
direction += 270;
break;
}
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.onOrientationChange(" + direction + ");");
}
// </interface SensorEventListener>
}

View File

@ -12,11 +12,9 @@ import android.preference.PreferenceManager;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.webkit.ConsoleMessage; import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions; import android.webkit.GeolocationPermissions;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient; import android.webkit.WebChromeClient;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
@ -92,7 +90,7 @@ public class IITC_WebView extends WebView {
*/ */
@Override @Override
public void onGeolocationPermissionsShowPrompt(String origin, public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) { GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false); callback.invoke(origin, true, false);
} }
@ -171,6 +169,7 @@ public class IITC_WebView extends WebView {
} }
} }
@TargetApi(19)
public void loadJS(String js) { public void loadJS(String js) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript(js, null); evaluateJavascript(js, null);