I have searched high and low and found so many examples of this, but unable to get it to work, my setup currently is:
Notifier.java
public class Notifier{
Context mContext;
Notifier(Context c) {
mContext = c;
}
#JavascriptInterface
public void showText()
{
Toast.makeText(mContext, "Some text!", Toast.LENGTH_LONG).show();
}
}
SearchLicenseActivity.java
public class SearchLicenseActivity extends Activity {
WebView webView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_license);
LoadSearch();
}
public void LoadSearch(){
webView = (WebView)findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new Notifier(this), "Android");
webView.loadUrl("javascript:Android.showText();");
}
}
So I am expecting a Toast to show. It may be worth noting that this SearchActivity gets created when a button on a previous Activity is clicked; so I want it to execute LoadSearch straight away and get the Toast from the JavaScript.
I hope one of you out there can cure my woes over this!
Edit: I am also not getting any errors in LogCat.
OK, it turns out webView must have a webpage loaded.
Even if the url is "about:blank" this seems to work. So...
webView.loadUrl("about:blank");
webView.loadUrl("javascript:Android.showText();");
...works.
Related
This question already has answers here:
Unfortunately MyApp has stopped. How can I solve this?
(23 answers)
Closed 2 years ago.
I am a novice Android Developer.
I have created a package-private class which extends Application, and contains the required code for specific functions. I basically want to display if the user-selected button is the correct choice or not, via a toast. Since I have to call this code for many activities, I just created a package-private class for it. However, on clicking the button, the app crashes. Please see the code given below for reference.
I cannot change the onClick method to non-static because if I do that, Android Studio shows an error, and if I change it to static, I am unable to use the method getApplicationContext(), because it is not accessible inside static blocks.
I think that using view.getContext() is causing the crash.
Is there any workaround, or a solution?
Your help would be greatly appreciated. Thanks :)
Here is the code for your reference.
activity.java:
public class activity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(functions.select);
functions.makeLayout(expression, buttons);
}
}
Here is the code which crashes the app.
functions.java:
class functions extends Application {
private static int idx;
public static View.OnClickListener select=new View.OnClickListener() {
#Override
public void onClick(View view) {
int selected_index=(int) view.getTag();
if(selected_index==idx)
{
Toast.makeText(view.getContext(), "Correct.", Toast.LENGTH_LONG).show();
((Button) view).setTextColor(Color.GREEN);
}
else
{
Toast.makeText(view.getContext(), "Wrong.", Toast.LENGTH_LONG).show();
((Button) view).setTextColor(Color.RED);
}
}
};
Okay, I figured out that it was not view.getContext() but the line int selected_index=(int) view.getTag(); which was causing the crash. I solved it first making it into a string and then int by using the following code:
String selected_index=view.getTag.toString();
int sidx=Integer.parseInt(selected_index);
When my MainFragment first loads, it checks for internet connectivity. If there is internet, it loads the content from online. However, if there is no internet connectivity, I replace the existing fragment MainFragment within the container main_browse_fragment with InternetConnectivityFragment.
Within InternetConnectivtyFragment, I have a Retry button that re-checks the internet, and if there is internet connectivity, I remove InternetConnectivityFragment and call popbackstack.
The problem is, when popbackstack is called, I dont know how to reload the online data from within MainFragment. The online data loading and internet check is done within onActivityCreated of MainFragment.java, but when popbackstack is called within InternetConnectivityFragment.java, the view of MainFragment is blank
Obviously this is because the fragment is not "created" and onActivityCreated is not called again, so no data is reloaded.
Here's the relevant code:
MainFragment.java:
public class MainFragment extends DetailsFragment
{
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setupUIElements();
if (isConnectingToInternet() == true)
{
// Call methods to load online data
}
else
{
InternetConnectivityFragment internetError = new InternetConnectivityFragment();
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, internetError).commit();
}
}
...
}
InternetConnectivityFragment.java:
public class InternetConnectivityFragment extends ErrorFragment
{
private static final String TAG = "InternetFragment";
private static final boolean TRANSLUCENT = true;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setErrorContent();
}
private void setErrorContent()
{
setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud, null));
setMessage(getResources().getString(R.string.no_internet_message));
setDefaultBackground(TRANSLUCENT);
setButtonText(getResources().getString(R.string.retry_connection));
setButtonClickListener(new View.OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (Singleton.getInstance().isConnectedToInternet() == true)
{
getFragmentManager().beginTransaction().remove(InternetConnectivityFragment.this).commit();
getFragmentManager().popBackStack();
// How to call methods to re-load online data from MainFragment????
}
}
});
}
}
I have tried to perform the following within the onClick method:
MainFragment mainFragment = new MainFragment();
getFragmentManager().beginTransaction().remove(InternetConnectivityFragment.this).commit();
getFragmentManager().popBackStack();
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mainFragment).commit();
However, I believe this is wrong as it just adds a new fragment MainFragment on top of an already existing fragment.
Can someone point me in the right direction?
Thanks
I have a crash report of the same error like in this question: WebView methods on same thread error
There it is suggested to create a Runnable().
I don't understand why exactly this solves the problem. The error says "Webview methods on same Thread", but the answer suggests to create the method on the UI-Thread (Main Thread). But isn't the UI-Thread the one and only thread? Could someone explain this whole process in detail (considering I create a new Webview in every activity in the constructor)?
My code to implement Javascript functions/methods looks like this:
public class JS_Bind {
private static final String TAG = "JS_Bind";
private Context context;
private AdvancedWebView mWebView;
public JS_Bind(Context c, AdvancedWebView mWebView) {
context = c;
this.mWebView = mWebView;
}
#JavascriptInterface
public void openActivity(String activityName) {
try {
Class activityClass = Class.forName(PACKAGE_NAME + "." + activityName);
context.startActivity(new Intent(MainActivity.this, activityClass));
} catch (ClassNotFoundException e) {
Toast.makeText(context, "Invalid activity name: " + activityName, Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
#JavascriptInterface
public void makeToast(String toast) {
Toast mToast = Toast.makeText(context, toast, Toast.LENGTH_SHORT);
mToast.setGravity(Gravity.CENTER, 0, 0);
mToast.show();
}
#JavascriptInterface
public void external(String url) {
mTracker.send(new HitBuilders.EventBuilder().setCategory("Action").setAction("External Link: " + url).build());
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
#JavascriptInterface
public String showToken() {
return gcmToken;
}
#JavascriptInterface
public int showUid() {
SharedPreferences pref = getSharedPreferences("Pref", Activity.MODE_PRIVATE);
int uid = pref.getInt("uid", 0);
return uid;
}
#JavascriptInterface
public void buyPremium() {
bp.purchase(MainActivity.this, PRODUCT_ID);
}
}
Do I have to change EVERY function to this code (first answer in the question I refered to):
#JavascriptInterface
mWebView.post(new Runnable() {
#Override
public void makeToast() {
// ...
}
});
?
By the way, this is how I create the webview in the constructor activies onCreate method:
mWebView = (AdvancedWebView) findViewById(R.id.webView);
mWebView.setListener(this, this);
mWebView.addJavascriptInterface(new JS_Bind(this, mWebView), "Android");
mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
if (!DetectConnection.checkInternetConnection(this)) {
mWebView.loadUrl("file:///android_asset/offline.html");
}
else {
mWebView.loadUrl("http://example.com/tw3/index.php?s=home");
}
But isn't the UI-Thread the one and only thread?
No. WebView has its own pool of threads. There can be many other threads in an Android application.
Do I have to change EVERY function to this code
Not necessarily.
First, I do not see how you are getting that error (A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread.) from your #JavascriptInterface methods shown above. That error is when you call a method on the WebView itself, and you are not doing that.
You will need to use runOnUiThread() (or equivalent techniques) if:
Your #JavascriptInterface methods refer to the WebView itself, or
Your #JavascriptInterface try to do something else that has to be done on the main application thread (which will yield a different error message, as it will not be tied specifically to WebView)
Your startActivity() call might need to be called on the main application thread — I forget if that can be called on a background thread or not. Similarly with your Toast work — while I think that can be done on a background thread, I am not certain of it. It has been ages since I tried doing either of those things from a background thread.
Also, please only use your code if you control every single byte of what is being displayed in the WebView. Exposing startActivity() to arbitrary Web content has significant security implications.
I am trying to make a webview where the app loads a HTML page from assets if there is no internet connection. I have been following this QUESTION and i have made a CheckNetwork.java but where shall i put this code below into my MainActivity? I am a beginner at this so please explain as simple as possible.
if(CheckNetwork.isInternetAvailable(MainActivity.this))
{
// do something
}
This is my MainActivity
public class MainActivity extends ActionBarActivity {
WebView browser;
private ourViewClient mClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mClass = new ourViewClient(this);
browser = (WebView) findViewById(R.id.wvwMain);
browser.getSettings().setJavaScriptEnabled(true);
browser.getSettings().setLoadWithOverviewMode(true);
browser.getSettings().setUseWideViewPort(true);
browser.setWebViewClient(new ourViewClient(this));
try {
browser.loadUrl("http://MyWebPage");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
This should do the trick for you:
if(CheckNetwork.isInternetAvailable(MainActivity.this))
browser.loadUrl("http://MyWebPage");
} else {
browser.loadUrl("file:///android_asset/your_html.html");
}
I am also including example of how you load a html from your assets.
This is for study and I just want to make sure my answers are 100% right. The question gives me skeleton code and I'm required to fill it in. Here is the code.
public class WebFragment extends WebViewFragment {
private WebView mWebView;
#Override
public void onActivityCreated(Bundle.savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Fill in here
}
public void refreshPage(View view) {
// Fill in here
}
}
Now I am asked three questions. I have put my answers below each question.
a. Instantiate the WebView property in the class
b. When the activity has started, make the WebView component load the url "http://www.google.com/"
#Override
public void onActivityCreated(Bundle.savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mWebView = (WebView) findViewById(R.id.webview); // question a
mWebView.getSettings().setJavaScriptEnabled(true); // question a
mWebView.loadUrl("http://www.google.com"); // question b
}
c. Assuming a button is available in the activity layout and runs refreshPage when pressed, make the function reload the WebView page and show a Toast with the message "Page has been refreshed!"
public void refreshPage(View view) {
mWebView.reload();
Toast toast = Toast.makeText(getApplicationContext(), "Page has been refreshed!"), Toast.LENGTH_SHORT().show();
}
Any feedback is appreciated.
answer a seems ok, for the toast you don't need the variable, unless you don't want to do something other than showing the toast
mWebView.reload();
Toast.makeText(getApllicationContext(),"foo",Toast.LENGTH_SHORT).show();
or pass a custom time in milliseconds
Toast.makeText(getApllicationContext(),"foo",2000).show();
you can also initialize a Toast variable and than call show on it, it works, it's just a useless variable if you're not going to do anything with it but it's not wrong
Toast toast = Toast.makeText(getApplicationContext(),"foo",1000);
toast.show();
oh, I don't know if it's required, but like this refreshPage method is not callable by anybody, maybe you want to set an OnClickListener to the button (that we assume is in the layout) so that refreshPage can be called
((Button)findViewById(R.id.buttonId)).setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v){
refreshPage(v);
}
});