Android WebView - Setting HTML Field focus using Javascript - javascript

I have an application with just a visible WebView component to it which is used to display some dynamically generated HTML (can run Javascript too). It's enabled for the WebView.
For a few pages I am trying to set the focus of one of the input text boxes after the page load has finished - not through Body onLoad(), but instead by a JS call after the page has finished loading i.e. in onPageFinished(). Most of the javascript executes fine
webview.loadURL("javascript:JSMethod();");
But when I use the same call to do a document.getElementById('text1').focus() - the cursor does reach the element but the Soft Keyboard won't pop out. The same javascript code when executed from a button on the page does have the desired effect.
I am attaching the source code which has 3 buttons
Focus Text1 - Put the cursor on the text1 and pops up the Softkeyboard.
Java Focus Text1 - Calls Java to execute the same JS. Only shows the cursor there and doesn't pop out the keyboard
Java Focus Text1 And Keyboard Open - Calls JS from Java and Forces Keyboard open.
My understanding is that the JS execution should work the same whether executed from the browser using a button/event or as sent from Java through WebView.loadURL().
Here's my Queries
Am I missing something when using Button#2? That's how my current code is and I only see the cursor is set but the SoftKeyboard won't open.
When I use logic as written in Button#3, I some times don't see the cursor on the field but gives me the desired effect and the cursor becomes visible when I type something in the popped up keyboard.
Could the behavior I see in Button#2 be a bug in Android JS execution? Or could it be that the WebView doesn't have focus that's why only the cursor is displayed? I also tried webview.requestFocus() - I can't write requestFocusOnTouch() as it's the only View I have and am expecting it's focused automatically.
The Java code which demos the behavior is
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class WebViewProjectTestJSHTMLFocusActivity extends Activity {
Activity _current = null;
WebView wv = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_current = this;
//setContentView(R.layout.main);
String data = "<html><body>" +
"<script>function focusser() { document.getElementById(\"text1\").focus(); } </script>" +
"<script>function javaFocusser() { javautil.javaFocus(false); } </script>" +
"<script>function javaFocusserKeyboard() { javautil.javaFocus(true); } </script>" +
"Text 1<input type='text' id='text1'/><br/>" +
"Text 2<input type='text' id='text2'/><br/>" +
"<input type='button' value='Focus Text1' onClick='focusser()'/>" +
"<input type='button' value='Java Focus Text1' onClick='javaFocusser()'/>" +
"<input type='button' value='Java Focus Text1 And Keyboard Open' onClick='javaFocusserKeyboard()'/>" +
"</body></html>";
wv = new WebView(this);
wv.getSettings().setJavaScriptEnabled(true);
// Set some HTML
wv.loadDataWithBaseURL("file:///android_asset/", data, "text/html", "UTF-8", null);
// Call back required after page load finished
wv.setWebViewClient(new CustomWebViewClient());
// Enable Alert calls
wv.setWebChromeClient(new CustomWebChromeClient());
// For JS->Java calls
wv.addJavascriptInterface(this, "javautil");
setContentView(wv);
}
/**
* Calls the same javascript and forces the keyboard to open
*/
public void javaFocus(final boolean shouldForceOpenKeyboard) {
Thread t = new Thread("Java focusser thread") {
public void run() {
wv.loadUrl("javascript:focusser();");
if(shouldForceOpenKeyboard) {
InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
}
}
};
// Run on the View Thread.
_current.runOnUiThread(t);
}
/**
* Calls focus method after the page load is complete.
*/
final class CustomWebViewClient
extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
// javaFocus(true);
Log.d("TestExamples", "focusser call complete");
}
}
final class CustomWebChromeClient
extends WebChromeClient {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("TestExamples", "JS Alert :: " + message);
return false;
}
}
}
Solution Update 24-06-2011
To make this work, you need to use wv.requestFocus(View.FOCUS_DOWN) just before the actual JS focus call. I modified the javaFocus() method above to the correct version below. Earlier when I mentioned that I was using requestFocus(), I was using that when the WebView was initialized in the method onCreate(). The primary difference is now we're forcing the WebView to get focus each time just before the Javascript document.getElementById("text1").focus(); is executed.
public void javaFocus(final boolean shouldForceOpenKeyboard) {
Thread t = new Thread("Java focusser thread") {
public void run() {
wv.requestFocus(View.FOCUS_DOWN);
wv.loadUrl("javascript:focusser();");
if(shouldForceOpenKeyboard) {
InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
}
}
};
// Run on the View Thread.
_current.runOnUiThread(t);
}
Also to ensure that this issue wasn't fixed because of focus triggered through touch etc, I am using a background thread to initiate the javaFocus() after 5 seconds of WebView Displayed. The modified onCreate() is below.
..... More onCreate code before....
// Enable Alert calls
wv.setWebChromeClient(new CustomWebChromeClient());
// For JS->Java calls
wv.addJavascriptInterface(this, "javautil");
setContentView(wv);
new Thread("After sometime Focus") {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
javaFocus(true);
}
}.start();
.... onCreate() ends after this....

It could be that the webview doesn't have the application focus. Try executing;
wv.requestFocus(View.FOCUS_DOWN);

Here is the complete solution.
protected void onCreate(Bundle savedInstanceState) {
// setfocus on page load finished
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(myWebView, url);
myWebView.requestFocus(View.FOCUS_DOWN);
Toast.makeText(getApplicationContext(), "Done!", Toast.LENGTH_SHORT).show();
}
});
}

Related

Communicate between Javascript and WebView native code in Xamarin.Forms iOS

OK so this works on Android... I'm stuck on iOS.
I have a custom HTML WebView which has a JavaScript event:
const string html= #"<button type=""button"" onClick=CSharp.SendButton(' + buttonCode + ')"">Click here</button>";
On Android, I have a webview custom renderer:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Control != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.AddJavascriptInterface(new JSBridge(Forms.Context), "CSharp");
}
base.OnElementPropertyChanged(sender, e);
}
And JSBridge is a bridge between Javascript and C#:
public class JSBridge : Java.Lang.Object
{
Context context;
public JSBridge(Context context)
{
this.context = context;
}
[Export]
[JavascriptInterface]
public void SendButton(string code)
{
// I can pass the buttonCode here
}
}
How can I do something similar on iOS?
Please note: I am aware of the HybridWebView examples on the Microsoft website: I dont want to use a seperate Javascript file, I want to fire the event from my HTML and catch it in my renderer.

How to remove the header in Android WebView? (While Loading)

I want to remove the header of the website in android 'WebView'. With the Code that I have, it works. But the problem is that the 'WebView' is removing the header after the page loaded completely. I want to remove it, while it is loading.
Thats the code snippet:
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
webView.loadUrl("javascript:(function() { " +
"var head = document.getElementsByTagName('header')[0];"
+ "head.parentNode.removeChild(head);" +
"})()");
webView.loadUrl("javascript:(function() { " +
"var head = document.getElementsByTagName('footer')[0];"
+ "head.parentNode.removeChild(head);" +
"})()");
}
});
ANDROID WebView-Remove header or any tag from Webpage in Android
Let's understand how's it possible, in my case I had loaded url in webview and look like below screenshot,
Now from the above view if we want to remove the header and menu
portion of the Webpage. We need to use JSOUP lib with the help of
which you can remove the unnecessary portion of webpage.
STEPS
Add jsoup lib in your app level build gradle
compile 'org.jsoup:jsoup:1.10.3'
Now In activity we will use Async task as we parse / remove some html tags that leads to network operations which cannot be done directly on UI Thread. Async Task allows you to perform background operations and publish results on the UI thread. It has three methods commonly used.
onPreExecute():-Is used to display progress bar
doInBackground():-This step is used to perform background computation that can take a long time. You cannot update any UI in this method.
onPostExecute():-This is invoked after completion of background task and it will update the UI.
I have defined WebView in xml like following way,
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webview"/>
And in MainActivity.java, I had wrote JSOUP code for remove the unnecessary portion of webpage
public class MainActivity extends AppCompatActivity {
WebView webview;
String url="http://pixelay.com/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webview= (WebView) findViewById(R.id.webview);
new MyAsynTask().execute();
}
private class MyAsynTask extends AsyncTask<Void, Void, Document> {
#Override
protected Document doInBackground(Void... voids) {
Document document = null;
try {
document= Jsoup.connect(url).get();
document.getElementsByClass("main-navigation").remove();
document.getElementsByClass("custom-header-image").remove();
} catch (IOException e) {
e.printStackTrace();
}
return document;
}
#Override
protected void onPostExecute(Document document) {
super.onPostExecute(document);
webview.loadDataWithBaseURL(url,document.toString(),"text/html","utf-8","");
webview.getSettings().setCacheMode( WebSettings.LOAD_CACHE_ELSE_NETWORK );
webview.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, request);
}
});
}
}
}
Now, it's time to see the result,
Easiest way is to inject Javascript in onLoadResource() method. Put it inside try-catch block since WebView will not know about the element before it has been loaded:
webView.setWebChromeClient(new WebChromeClient() {
...
#Override
public void onLoadResource(WebView view, String url) {
try {
webView.loadUrl("javascript:(window.onload = function() { " +
"(elem1 = document.getElementById('id1')); elem.parentNode.removeChild(elem1); " +
"(elem2 = document.getElementById('id2')); elem2.parentNode.removeChild(elem2); " +
"})()");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class YourwebView extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.getUrl());
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:var footer = document.getElementById(\"footer\"); footer.parentNode.removeChild(footer); var header = document.getElementById(\"header-full\"); header.parentNode.removeChild(header);");
}
}
And also make sure you have enabled javscript
YourwebView myWebClient = new YourwebView();
webview.setWebViewClient(myWebClient);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
Use onPageStarted method instead of onPageFinished.
From the Android Docs here
EDIT: Reading the comments on other answers, I see you want the header to show again. Simply implement both onPageStarted and onPageEnded, hiding the header in one and showing it again in the other.

Android Webview doesn't load HTML sometimes

I am trying to load local HTML content in my webview. But sometimes it failed to load the content instead shows blank screen. It happens ones in every 5 times of loading.
NOTE My HTML content, I am trying to load is Official 2048 Source code.
Below is my Activity source code
public class GameActivity extends AppCompatActivity {
private WebView mWebView;
#SuppressWarnings("ConstantConditions")
#SuppressLint({ "SetJavaScriptEnabled", "NewApi", "ShowToast"})
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
setContentView(R.layout.activity_game);
Toolbar toolbar = (Toolbar) findViewById(R.id.game_toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
// Load webview with game
mWebView = (WebView) findViewById(R.id.mainWebView);
WebSettings settings = mWebView.getSettings();
String packageName = getPackageName();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);
settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
if (Build.VERSION.SDK_INT >= 19) {
// chromium, enable hardware acceleration
mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else {
// older android version, disable hardware acceleration
mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
// Since the database path is automatically handled by Chromium Webkit,
// we should not mention the db path for greater than KitKat version
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
settings.setDatabasePath("/data/data/" + packageName + "/databases");
}
mWebView.addJavascriptInterface(new WebInterface2048(this), "Android");
// If there is a previous instance restore it in the webview
if (savedInstanceState != null) {
mWebView.restoreState(savedInstanceState);
} else {
mWebView.loadUrl("file:///android_asset/2048/index.html");
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
}
return super.onOptionsItemSelected(item);
}
public class WebInterface2048 {
Context mContext;
public WebInterface2048(Context context) {
mContext = context;
}
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
}
Till now I have tried the below things to solve the problem:
Put Hardware Acceleration on the of Manifest file.
Enabled and Disabled Hardware Acceleration inside Activity.
Reloaded the same URL inside shouldOverrideUrlLoading
Tried to load the URL inside onStart() instead of onCreate()
But nothing seems to be working for me.
My Logs:
D/OpenGLRenderer: endAllActiveAnimators on 0xb7d7e248 (RippleDrawable) with handle 0xb76b0cf0
I/AppCompatViewInflater: app:theme is now deprecated. Please move to using android:theme instead.
D/cr_Ime: [InputMethodManagerWrapper.java:30] Constructor
W/cr_AwContents: onDetachedFromWindow called when already detached. Ignoring
D/cr_Ime: [InputMethodManagerWrapper.java:59] isActive: false
I/cr_Ime: ImeThread is not enabled.
W/cr_BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 18631
D/cr_Ime: [InputMethodManagerWrapper.java:59] isActive: true
D/cr_Ime: [InputMethodManagerWrapper.java:68] hideSoftInputFromWindow
D/OpenGLRenderer: endAllActiveAnimators on 0xb7a893f8 (RippleDrawable) with handle 0xb7ec8810
I/AppCompatViewInflater: app:theme is now deprecated. Please move to using android:theme instead.
D/cr_Ime: [InputMethodManagerWrapper.java:30] Constructor
W/cr_AwContents: onDetachedFromWindow called when already detached. Ignoring
D/cr_Ime: [InputMethodManagerWrapper.java:59] isActive: false
I/cr_Ime: ImeThread is not enabled.
W/cr_BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 18631
D/cr_Ime: [InputMethodManagerWrapper.java:59] isActive: true
D/cr_Ime: [InputMethodManagerWrapper.java:68] hideSoftInputFromWindow
Additional Info: My Device is Moto G3 (Android 6.0.1)
Never understood why this is happening. But as a workaround, I came out of this problem using below code:
mWebView.postDelayed(new Runnable() {
#Override
public void run() {
mWebView.loadUrl("file:///android_asset/2048/index.html");
}
}, 500);
Instead of loading the URL in main thread, if I am running in a different thread, it worked for me.
The above work around can be helpful for those, who are facing problem, where their webview shows blank when loads first time and on reloading, it loads the content.
May be its not a prob=per solution, but it worked. If anybody is finding a better solution, please do post.
I solved the issue by manual adding WebView into the layout within onCreate method:
LinearLayout ll = findViewById(R.id.content);
WebView help = new WebView(this);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
help.setLayoutParams(lp);
ll.addView(help);
Then load html as usual:
try {
String base64 = Base64.encodeToString(sampleHtml.getBytes("UTF-8"), Base64.DEFAULT);
help.loadData(base64, "text/html; charset=utf-8", "base64");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception ex){
ex.printStackTrace();
}
Try this isolate from the rest of the code. Put it in oncreate by itself to test. saveinstancestate is not always null so it doesn't load.
mWebView.loadUrl("file:///android_asset/2048/index.html");

GeoLocation in Android Webview

I have an Android app that uses a web view. The web view works well, except for the JS that uses getCurrentPosition to locate the user. I have read about this issue and understand that I need to have the right permissions, and that I also have to set setJavaScriptEnabled(true);. I have done these things, but am still having a lot of trouble with my Android app. Sometimes the prompt that requests permission to obtain the user's location doesn't even come up. Can anyone help me fix this? I have included the relevant Java file, the permissions from my manifest.xml, and the JS that gets the user's location. Any help would be wonderful!
Here is my Java file:
package com.website.appname;
import android.app.Activity;
//OTHER IMPORTS HERE
class MyClient extends WebChromeClient {
#Override
public void onGeolocationPermissionsShowPrompt(String origin,
Callback callback) {
callback.invoke(origin, true, false);
}
}
public class locationPage extends Activity {
WebView webView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.embed);
webView = (WebView) findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
webView.requestFocus(View.FOCUS_DOWN);
webView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
});
webView.loadUrl("http://www.website/page.php");
webView.setWebChromeClient(new MyClient());
#SuppressWarnings("unused")
class HelloWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
Here are the permissions in my manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
And here is the webpage that is loaded by the web view:
<script>
if( navigator.geolocation ) {
// Call getCurrentPosition with success and failure callbacks
navigator.geolocation.getCurrentPosition( success, fail );
}
else {
alert("Sorry, your browser does not support geolocation services.");
}
function success(position) {
// GeoPosition Object
window.location = "another_page.php?user_lat=" + position.coords.latitude
+ "&user_lon=" + position.coords.longitude + "&accuracy=" +
position.coords.accuracy;
}
function fail() {
// Could not obtain location
}
</script>
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
P.S. I have also tried looking at things like android webview geolocation, which were helpful, but my issue still persists. Thank you!
It looks like geolocation is timing out before a position i generated. Try setting a timeout period to delay the default timeout period.
navigator.geolocation.getCurrentPosition(success,
fail,
{timeout:60000});

JavaScript doesn't work in Android WebView

I want to load a url by webView.
The url is http://wapp.baidu.com/f?kw=%BB%F0%BC%FD ,this page can work correctly on system default browser, but in my webView, some JavaScript not working.
JavaScript is enabled, and I dont know what's wrong with it.
I would appreciate any help.
private void initUI() {
webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
final NavigationBar navigationBar = (NavigationBar) findViewById(R.id.navigationbar);
navigationBar.refreshUI();
navigationBar.rightButton.setImageResource(R.drawable.refresh);
navigationBar.rightButton.setVisibility(View.VISIBLE);
navigationBar.rightButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
refresh();
}
});
navigationBar.leftButton.setImageResource(R.drawable.back);
navigationBar.leftButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
webView.goBack();
}
});
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
public void onPageFinished(WebView view, String url) {
navigationBar.leftButton.setVisibility(webView.canGoBack() ? View.VISIBLE
: View.INVISIBLE);
super.onPageFinished(view, url);
}
});
refresh();
}
update: the problem is solved
just add following code:
webView.getSettings().setDomStorageEnabled(true);
If you're using proguard, it stops javascript running. For instance, in my proguard-project.txt file I have the following:
if your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
I think there is some issue with javascript file caching/refreshing. I changed the filename of js & it starts working fine.

Categories