WebViewClient not called the second time - javascript

I have a WebView which I use to load some html content locally in my app. It first loads the content, then calls a JavaScript function which then scrolls the WebView to a particular position.
The following code illustrates how I do this:
public class MyActivity extends Activity {
private WebView web1;
private int ID;
private MyWebViewClient webViewClient1;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
// Get the ID of the law to be loaded.
ID = getIntent().getIntExtra("element_id", 1);
web1 = (WebView) findViewById(R.id.web1);
web1.getSettings().setJavaScriptEnabled(true);
// Initialize the webViewClients.
webViewClient1 = new MyWebViewClient(true);
web1.setWebViewClient(webViewClient1);
displayArticle(web1);
}
private void displayArticle (WebView wv) {
StringBuilder sb = new StringBuilder();
// Code to build the HTML String.
String finalHtml = sb.toString();
wv.loadDataWithBaseURL("file:///android_asset/html/", finalHtml, "text/html", "UTF-8", null);
}
private class MyWebViewClient extends WebViewClient {
String urlToLoad;
MyWebViewClient (boolean setUrlToLoad) {
if (setUrlToLoad) {
setUrlToLoad();
}
}
public void setUrlToLoad () {
this.urlToLoad = "javascript:(function () {" +
"var elem = document.getElementById('e"+ID+"');" +
"var x = 0;" +
"var y = 0;" +
"while (elem != null) {" +
"x += elem.offsetLeft;" +
"y += elem.offsetTop;" +
"elem = elem.offsetParent;" +
"}" +
"window.scrollTo(x, y);" +
"})()";
}
#Override
public void onPageStarted (WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("Pages", "Page loading started");
}
#Override
public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Log.d("Pages", "Webview content load error");
}
#Override
public void onPageFinished (WebView view, String url) {
super.onPageFinished(view, url);
Log.d("Pages", "Page loading finished");
if (urlToLoad != null) {
// Scroll to the position.
view.loadUrl(urlToLoad);
urlToLoad = null;
}
}
}
}
In the above code, the callback functions in the MyWebViewClient class are called for the first request using wv.loadDataWithBaseURL in the displayArticle(WebView wv) function, but when the request is finished and onPageFinished is called, the view.loadUrl(urlToLoad); call does not invoke another set of callbacks from MyWebViewClient. I am not quite sure why since it must be the same WebView I originally used and it should have the same instance of MyWebViewClient set.
Moreover, there are other loadUrl calls that I make with the same WebView, and this behaviour persists.
I would really appreciate if someone could explain why this happens.

loadUrl("javascript:...") is a bit of a special case: it evaluates the JavaScript code in the current page's context (just like <a href='javascript:...'>clicky</a> would) and therefore you won't get onPageStarted/onPageFinished callbacks.

Related

Display append function in another new activity

I am doing an application which contains a listview of phone contact with a checkbox. I have found a coding that suits to my project. It works well but it does not operate as i want. I dont want 'respondText' appear in toast function, i just want it displayed in another new activity after the button clicked. I have tried the intent function, i put it under the toast funstion but the application does not perform. Maybe i just misplaced the function, what is the actual solution to handle this problem? Here is it :
private void checkButtonClick() {
Button myButton = (Button) findViewById(R.id.findSelected);
final TextView textView = (TextView) findViewById(R.id.showAppend);
myButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
StringBuffer responseText = new StringBuffer();
responseText.append("The following were selected...\n");
ArrayList<Contacts> countryList = dataAdapter.contactList;
for (int i = 0; i < countryList.size(); i++) {
Contacts country = countryList.get(i);
if (country.isSelected()) {
responseText.append("\n" + country.getName());
}
}
//Toast.makeText(getApplicationContext(), responseText,
//Toast.LENGTH_LONG).show();
Intent startNewAct = new Intent(ListViewCheckboxesActivity.thisthis, DisplayAppendActivity.class);
startActivity(startNewAct);
textView.setText(responseText);
}
});
}
Here is another new activity:
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class DisplayAppendActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.displayappend);
TextView textView = (TextView) findViewById(R.id.showAppend);
String responseText = getIntent().getExtras().getString("responseText");
textView.setText(responseText);
}
}
You can send data through Intent. When starting actvity from intent you can use:
Intent startNewAct = new Intent(ListViewCheckboxesActivity.this, DisplayAppendActivity.class);
startNewAct.putExtra("responseText", responseText);
startActivity(startNewAct);
And you can get this responseText in another activity onCreate:
String responseText = getIntent().getExtras().getString("responseText");

Save in sharedPrefs from a class

hi all i've got this code to save some datas in sharedprefs but android studio tells me "getDefaultSharedPrefs in Preference Manager cannot be applied to com.foo.downloadDB.AttemptLogin" how can i do to solve this error?
i seems like i can't save to my sharedprefs from a "child" class
here is the code:
public class downloadDB extends Activity {
private EditText user, pass;
private android.widget.Button mSubmit, mRegister;
// Progress Dialog
private ProgressDialog pDialog;
//JSON parser class
JSONParser jsonParser = new JSONParser();
//php login script location:
//localhost :
//testing on your device
//put your local ip instead, on windows, run CMD > ipconfig
//or in mac's terminal type ifconfig and look for the ip under en0 or en1
// private static final String LOGIN_URL = "http://xxx.xxx.x.x:1234/webservice/login.php";
//testing on Emulator:
private static final String LOGIN_URL = "http://www.myurl.com/users.php";
//testing from a real server:
//private static final String LOGIN_URL = "http://www.yourdomain.com/webservice/login.php";
//JSON element ids from repsonse of php script:
private static final String TAG_SUCCESS = "success";
private static final String TAG_MESSAGE = "message";
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download_db);
new AttemptLogin().execute();
}
public class AttemptLogin extends AsyncTask<String, String, String> {
boolean failure = false;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor1 = sharedPreferences.edit();
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(downloadDB.this);
pDialog.setMessage("Download..");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
#Override
protected String doInBackground(String...args) {
//Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
Log.d("request!", "starting");
// getting product details by making HTTP request
JSONObject json = jsonParser.makeHttpRequest(
LOGIN_URL, "POST", params);
//check your log for json response
Log.d("Login attempt", json.toString());
editor1.putString("JSON_DB", json.toString());
editor1.commit();
System.out.println(editor1.commit());
return null;
}
// After completing background task Dismiss the progress dialog
protected void onPostExecute(String file_url) {
//dismiss the dialog once product deleted
pDialog.dismiss();
if (file_url != null) {
Toast.makeText(downloadDB.this, file_url, Toast.LENGTH_LONG).show();
}
// Intent intent = new Intent(downloadDB.this, SampleActivity.class);
// startActivity(intent);
}
}
}
EDIT
I have this method in my SampleActivity class
private void LoadPreferences(){
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String name = sharedPreferences.getString("name", "0") ;
String email = sharedPreferences.getString("email", "0") ;
String id = sharedPreferences.getString("id", "0") ;
String Database = sharedPreferences.getString("JSON_DB", "0") ;
Toast.makeText(this, Database, Toast.LENGTH_LONG).show();
//Toast.makeText(this, id, Toast.LENGTH_LONG).show();
//Toast.makeText(this, email, Toast.LENGTH_LONG).show();
}
and the Toast works fine, it prints me the JSON_DB correctly, but when i try to reach it from the respective Fragment it doesn't work.. here is my fragment code :
public class SampleFragment extends Fragment {
public static String KEY_ID = "id";
public static String KEY_EMAIL = "email";
public static String KEY_NAME = "name";
public static String JSON_DB = "";
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LoadPreferences();
Log.i("string", KEY_EMAIL);
Log.i("string", KEY_ID);
Log.i("string", KEY_NAME);
Log.i("string", JSON_DB);
and KEY_EMAIL, KEY_ID and other objects logs correctly..
This is the problem:
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
this is the instance of the AsyncTask instead Activity
use downloadDB.this instead only this; but please call your class using Uppercase (DownloadDB)

Unable To Insert An Array Selection into a Database

First off, I would just like to mention that my problem isn't as easy as the title suggests. Here is my problem. Basically I am trying to set the value of a string to a randomly selected item in a string array. Shown here:
String[] array = getResources().getStringArray(R.array.states);
selected_state = array[new Random().nextInt(array.length)];
When a button is pushed, this string is insert into my database. Like this:
long id = database_helper.insertData(selected_state);
It seem like it should work, but unfortunately for me it does not. Apparently selected_state is null when I try to insert it. But another string in my app that is pulled from an editText and inserted the exact same way is totally fine. Here are the relevant parts of my code:
public class my_class extends Fragment implements View.OnClickListener {
View rootView;
Button bing;
EditText text;
public String selected_state;
public String TEXT;
DatabaseAdapter database_helper;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.my_fragment, container, false);
bing = (Button) rootView.findViewById(R.id.my_btn);
text = (EditText) rootView.findViewById(R.id.my_text_edit);
String[] array = getResources().getStringArray(R.array.states);
selected_state = array[new Random().nextInt(array.length)];
bing.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
send();
}
private void send() {
TEXT = text.getText().toString();
//VALUE HOLDS UP TO HERE
long id = database_helper.insertData(TEXT, selected_state);//NULL!!!!!!
if(id<0){
Toast.makeText(getActivity(), "SHES STUCK", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getActivity(), "SHES IN", Toast.LENGTH_LONG).show();
text.setText("");
}
}
});
return rootView;
}
#Override
public void onClick(View v) {
}
}
Thanks everyone!
EDIT The string (or what I believe to be a string) value has been carried down to the same place TEXT is. Marked above

Wicket 6.2 AbstractDefaultAjaxBehavior getCallbackUrl no longer resolves JS variables

Recently I have been working on upgrading a big web application that was using wicket 1.4.18 to 6.2. We had a situation where we would create javascript variables to keep track of positioning within a drag and drop list. This is just the wicket side of the code since the js has always worked and has not been changed.
ListItem.add(new AbstractDefaultAjaxBehavior()
{
private static final long serialVersionUID = 1L;
#Override
public void onComponentTag(ComponentTag tag)
{
tag.put("ondrop", "var value = $(ui.item[0]).attr('hiddenvalue');"
+ this.getCallbackScript());
}
#Override
public final CharSequence getCallbackUrl()
{
return super.getCallbackUrl() + "&hiddenvalue' + value + '";
}
}
However the problem I am running into is the javascript variables are not resolving to values and are now being taken as literal strings (Ex: 'value' instead of 5) in the getCallbackUrl. This was not the case in wicket 1.4.18 and I don't believe this problem originated in our migration to 1.5.8.
In the end we just want to be able to pull the value out using
#Override
protected void respond(AjaxRequestTarget target)
{
getRequest().getRequestParameters().getParameterValue("hiddenvalue");
}
Any advice on this? I hope I have provided enough information.
Thanks in advance for any help. Some of this is a little beyond my knowledge and can be intimidating not knowing where to look.
Wicket Ajax has been completely rewritten for Wicket 6. See this page for a detailed description.
In your case, you should use the new AjaxRequestAttributes like that:
#Override
protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.getExtraParameters().put("hiddenvalue", "value");
}
Retrieval of the value from the request still works the same as before.
#Override
protected void respond(AjaxRequestTarget target)
{
getRequest().getRequestParameters().getParameterValue("hiddenvalue");
}
Another cleaner approach is to use the callback function
AbstractDefaultAjaxBehavior ajaxBehavior = new AbstractDefaultAjaxBehavior() {
#Override
protected void respond(AjaxRequestTarget target) {
String param1Value = getRequest().getRequestParameters().getParameterValue(AJAX_PARAM1_NAME).toString();
String param2Value = getRequest().getRequestParameters().getParameterValue(AJAX_PARAM2_NAME).toString();
System.out.println("Param 1:" + param1Value + "Param 2:" + param2Value);
}
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
String callBackScript = getCallbackFunction(CallbackParameter.explicit(AJAX_PARAM1_NAME), CallbackParameter.explicit(AJAX_PARAM2_NAME)).toString();
callBackScript = "sendToServer="+callBackScript+";";
response.render(OnDomReadyHeaderItem.forScript(callBackScript));
}
};
add(ajaxBehavior);
Define a variable for the function in your javascript
var sendToServer;
It will be initialized on dom ready event by wicket with the callback function
Call sendToServer(x,y) from javascript to pass the parameters to the server.
private static final String MY_PARAM = "myparam";
public static class SampleCallbackBehavior extends AbstractDefaultAjaxBehavior {
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
response.render(OnDomReadyHeaderItem.forScript("var myfunction : " + getCallbackFunction(CallbackParameter.explicit(MY_PARAM))));
}
#Override
protected void respond(AjaxRequestTarget target) {
StringValue paramValue = getComponent().getRequest().getRequestParameters().getParameterValue(MY_PARAM);
//TODO handle callback
}
}
After this, you should only call the function from javascript
myfunction("paramValue");

Android Phonegap: Notify javascript when an AsyncTask is finished

in my app, when user click on a button in webview, a phonegap plugin will be called to trigger an asynctask to download file from internet. Now i want to send a signal back to javascript part when the asynctask is finished. But i don't know how to do it, because my plugin had already send something back before the asynctask is finished. Does anyone know how i can notify my javascript part without plugin in Phonegap?
I also asked this question in Phonegap Google Group, here is response of Simon Mac Donald. It works perfectly for me:
You can handle this situation by using the Plugin API quite easily. It
is implemented in the core API items Connection and Battery. What you
need to do is:
1) In your execute() method of your plugin save the callbackId you get.
2) Return a NO_RESULT plugin result and set keep callback id to true.
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
pluginResult.setKeepCallback(true);
return pluginResult;
3) When you async java method finishes return another plugin result like this:
PluginResult result = new PluginResult(PluginResult.Status.OK, data);
result.setKeepCallback(false);
this.success(result, this.myCallbackId);
As I said, you can look at the code in GitHub to see how we are using this for Connection and Battery.
This is how I solve problems like your.
1) Create and associate a JavascriptInterface to your WebView. A JavascriptInterface is simply a class inside which you can declare some Java method you want to use from JS.
public class JSInterface() {
private final CountDownLatch latch = new CountDownLatch(1);
public void waitForProceed() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void canProceed() {
latch.countDown();
}
}
2) In your AsyncTask, at the end of onPostExecute() method, you have to call the canProceed() method to notify to JSInterface that it can exit from waitForProceed() method.
public class MyAsyncTask extends AsyncTask<.......> {
private JSInterface jsi;
... // other class property
public MyAsyncTask(JSInterface jsi) {
...
//do what you want with other class property
this.jsi = jsi;
}
#Override
public ... doInBackground(...) {
...
//do something
}
#Override
public void onPostExecute(...) {
...
//do something
jsi.canProceed();
}
}
3) In your Activity you have to associate the JSInterface object to your WebView:
WebView mWebView;
...
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "JSIface");
4) Finally, in JS, you can call AsyncTask (I don't know how you call it, but I guess you use somthing like a JSInterface) and after call waitForProceed() method:
startAsyncTask(); //somehow
JSIface.waitForProceed();
I hope it solves your problem ;)
Here is a detailed example:
Lets create some interface that should be called when AsyncTask will finish the stuff, a.e when onPostExecute called.
In my case we fetch some JSONArray data
MyTaskListenerItf.java
public interface GroupTaskListenerItf {
public void onTaskDone(JSONArray groupArray);
}
The AsyncTask template of looks like:
MyBuildTask.java
public class MyBuildTask extends AsyncTask<Void, Void, SomeData>{
private MyTaskListenerItf mTl = null;
public MyBuildTask(Context context, MyTaskListenerItf tl) {
super();
this.mContext = context;
this.mTl = tl;
}
#Override
protected SomeData doInBackground(Void... params) {
/* ... */
}
#Override
protected void onPostExecute(WmTransferItem transferItem) {
// ...
if(this.mTl != null){
JSONArray data = new JSONArray("");
this.mTl.onTaskDone(data);
}
// ..
}
}
So now our CordovaPlugin class should look like:
MyCordovaPlugin.java
public class MyCordovaPlugin extends CordovaPlugin implements GroupTaskListenerItf {
// we need this callback when Task will finish
private CallbackContext mMyCallbackContext = null;
#Override
public boolean execute(String action, JSONArray args,CallbackContext callbackContext) throws JSONException {
if("runMe".equals(action)){
final GroupTaskListenerItf gt = this;
mMyCallbackContext = callbackContext;
// pass our 'GroupTaskListenerItf' interface to async class
MyBuildTask task = new MyBuildTask(cordova.getActivity(), gt);
task.execute();
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
pluginResult.setKeepCallback(true);
callbackContext.sendPluginResult(pluginResult);
}
else{
this.cordova.getThreadPool().execute( new Runnable() {
public void run() {
// BTW, here you might run something else out of UI Thread
}
});
}
}
/* ... */
#Override
public void onTaskDone(JSONArray data) {
if (this.mGroupCallbackContext != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, data);
result.setKeepCallback(false);
this.mMyCallbackContext.sendPluginResult(result);
}
}
That's all.
Hope it will help to someone.

Categories

Resources