Phonegap: calling a java function from Javascript - java

I want to call one of my Java functions in Javascript and get its result. In order to do that I followed this tutorial and this question. I followed them step by step and I still get this error
Cannot call method 'showKeyBoard' of undefined
Here is my java class:
package keyboard;
import org.apache.cordova.DroidGap;
import android.content.Context;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
public class KeyBoard {
private WebView mAppView;
private DroidGap mGap;
public KeyBoard(DroidGap gap, WebView view) {
mAppView = view;
mGap = gap;
}
public void showKeyBoard() {
InputMethodManager mgr = (InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE);
// only will trigger it if no physical keyboard is open
mgr.showSoftInput(mAppView, InputMethodManager.SHOW_IMPLICIT);
((InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(mAppView, 0);
}
public void hideKeyBoard() {
InputMethodManager mgr = (InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.hideSoftInputFromWindow(mAppView.getWindowToken(), 0);
}
}
Here is my Main class:
package com.example.helloworld;
import keyboard.KeyBoard;
import android.os.Bundle;
import org.apache.cordova.*;
import android.view.Menu;
import QR.*;
public class MainActivity extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
KeyBoard keyboard = new KeyBoard(this, appView);
appView.addJavascriptInterface(keyboard, "KeyBoard");
super.loadUrl("file:///android_asset/www/index.html");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
And I call it in Javascript like this:
(function(){
window.KeyBoard.showKeyBoard();
})();
Is there anything that I haven't done or am missing? As I said I get this error:
Cannot call method 'showKeyBoard' of undefined

I recommend that you write a PhoneGap plugin instead of trying to roll your own method. We've already gone through all the pain points of the JavaScript to Java communication. Use what we've already written and you won't run into the Android bugs that we've already smoothed over in the past 3 years.
http://docs.phonegap.com/en/2.1.0/guide_plugin-development_index.md.html#Plugin%20Development%20Guide

In phonegap i recommend you using a custom plugin do this
but still if you want to make a direct call to Java see this example to get a general idea
public class MainActivity extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setIntegerProperty("loadUrlTimeoutValue", 70000);
super.loadUrl("file:///android_asset/www/index.html");
super.appView.addJavascriptInterface(new Bridge(), "b");
}
}
class Bridge {
#JavascriptInterface
public String a()
{
Log.i("Bridge","This is from js");
return "This is a message";
}
}
in javascript
setTimeout(function(){
alert(b.a());
}, 1000);
#JavascriptInterface annotation is required in you code to make this work .
package keyboard;
import org.apache.cordova.DroidGap;
import android.content.Context;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
public class KeyBoard {
private WebView mAppView;
private DroidGap mGap;
public KeyBoard(DroidGap gap, WebView view) {
mAppView = view;
mGap = gap;
}
/*make it visible in bridge*/
#JavascriptInterface
public void showKeyBoard() {
InputMethodManager mgr = (InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE);
// only will trigger it if no physical keyboard is open
mgr.showSoftInput(mAppView, InputMethodManager.SHOW_IMPLICIT);
((InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(mAppView, 0);
}
/*make it visible in bridge*/
#JavascriptInterface
public void hideKeyBoard() {
InputMethodManager mgr = (InputMethodManager) mGap.getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.hideSoftInputFromWindow(mAppView.getWindowToken(), 0);
}
}
And in Javascript call it like this:
(function(){
KeyBoard.showKeyBoard();
})();

I'm struggling with JavascriptInterface too. The reason why you cant call showKeyboard is IMHO you should call window.showKeyBoard() instead of window.Keyboard.showKeyBoard().

Related

null pointer exception at getRingerMode()

I wrote this app where users can toggle silent mode by clicking on an image button:
package p.a;
import android.media.AudioManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
ImageView imageView;
AudioManager audioManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.imageView);
final AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
View.OnClickListener onClickListener = new View.OnClickListener(){
#Override
public void onClick(View view) {
mode.toggle(audioManager);
update();
}
};
imageView.setOnClickListener(onClickListener);
}
public void update(){
int mod=mode.phonesilent(audioManager)?
R.drawable.ringer_off:
R.drawable.ringer_on;
}
}
Here's the mode class:
package p.a;
import android.media.AudioManager;
/**
* Created by root on 9/19/17.
*/
public class mode {
public static boolean phonesilent(AudioManager audioManager){
return audioManager.getRingerMode()==AudioManager.RINGER_MODE_SILENT;
}
public static void toggle(AudioManager audioManager){
int mode = phonesilent(audioManager)?
AudioManager.RINGER_MODE_NORMAL:
AudioManager.RINGER_MODE_SILENT;
}
}
The app can be initialized normally, but when I click on the image icon, it crashes and produces the error message in the title.
Here's the logcat:
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.media.AudioManager.getRingerMode()' on a null object reference
at p.a.mode.phonesilent(mode.java:11)
at p.a.MainActivity.update(MainActivity.java:29)
at p.a.MainActivity$1.onClick(MainActivity.java:22)
Firstly create a Mode Object like this :
Mode mode = new Mode();
You can do it as Vamshi Krishna says, it's totally a good answer for this problem, but if you don't want to instantiate it for some reason you could change "Mode class" to a "Mode static class" as follows:
package p.a;
import android.media.AudioManager;
/**
* Created by root on 9/19/17.
*/
public static class mode {
public static boolean phonesilent(AudioManager audioManager){
return audioManager.getRingerMode()==AudioManager.RINGER_MODE_SILENT;
}
public static void toggle(AudioManager audioManager){
int mode = phonesilent(audioManager)?
AudioManager.RINGER_MODE_NORMAL:
AudioManager.RINGER_MODE_SILENT;
}
}
Hope it helps!

Android: onCreate boolean not changing

In an android application that I am developing Im using a thread, and to make sure I dont get the "java.lang.IllegalStateException: System services not available to Activities before onCreate()" I use a boolean called donecreate. Problem is that Android studio says I have a "java.lang.NullPointerException at picLoop.run(picLoop.java:24)"
Code main class:
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
public class main extends Activity {
public Boolean donecreate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new eyeCanvas(this));
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
docreate();
}
public void docreate(){
donecreate = true;
}
public void checkHead(){
AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
if(am.isWiredHeadsetOn()){
Toast.makeText(getApplicationContext(), "HEADPHONES", Toast.LENGTH_LONG).show();
}
}
}
Code: pic loop
import android.graphics.Canvas;
//**Threading
public class picLoop extends Thread {
private eyeCanvas eye;
private main main = new main();
public picLoop(eyeCanvas eye) {
this.eye = eye;
}
#Override
public void run(){
Canvas c = null;
while(true) {
if(main.donecreate){ //<-- where error is
main.checkHead();
}
try {
// head.onCreate(Bundle savedInstanceState);
c = eye.getHolder().lockCanvas();
synchronized (eye.getHolder()) {
eye.onDraw(c);
}
} finally {
if (c != null) {
eye.getHolder().unlockCanvasAndPost(c);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Also if you guys could give me feedback on how I submitted, It would help :)
You can't create activities like you're trying to do. You can NEVER EVER do 'new Activity()', as the activity needs to be launched by the system to get set up properly and go through its lifecycle as intended.
So remove the line private main main = new main();.
To do what you're trying, make the boolean a static variable.
Change
public Boolean donecreate;
to
public static Boolean donecreate;
Then you can access it like you're trying to do, without creating an instance of main Activity.
There are a large number of things wrong with the assumptions you're making. Firstly, if your Thread requires your Activity to be created, don't start it until your Activity is created. Manage the lifecycle of this object within the Activity itself, i.e.:
#Override
public void onStart() {
super.onStart();
// Start your work here
}
#Override
public void onStop() {
super.onStop();
// Stop your work here
}
Secondly, please don't use the static access approach being recommended -- that makes the assumption that there is only one Activity instance (which is wrong immediately on a configuration change, and/or if you start another instance of that Activity in the task). And even if that assumption were true, you would need to set it back to false in onDestroy() (still, don't do that).
try setting donecreate to false initially
public class main extends Activity {
public Boolean donecreate = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
You can request system service on application context, look at this answer.
So create a static variable in application class, initialize it like instance = this; in onCreate of Application class and then you'll be able to get app context whenever you want.

My Junit test doesn't run

I'm new to Java, Android and JUnit. I want to learn how to write JUnit tests for an Android application. To that end, I have a very simple Android app (2 activities, 2 buttons, each button goes to the other activity). I want to test the button. This app runs fine on my phone when it's plugged in. I've been looking at the samples provided in the SDK, and I am trying to emulate them.
My problem is that when I right-click on my test project, and choose Run As -> Android JUnit test, nothing happens. I don't know why.
My test code.
package com.example.twoactivities.test;
import android.app.Instrumentation.ActivityMonitor;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.Button;
import com.example.twoactivities.MainActivity;
import com.example.twoactivities.MainActivity2;
public class ClickButton extends ActivityInstrumentationTestCase2<MainActivity> {
private Button mButton2;
private long TIMEOUT_IN_MS = 100000;
public ClickButton() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
final MainActivity a = getActivity();
// ensure a valid handle to the activity has been returned
assertNotNull(a);
}
#SmallTest
public void click(){
// Set up an ActivityMonitor
ActivityMonitor activityMonitor = getInstrumentation().addMonitor(MainActivity2.class.getName(), null, false);
//check if button is enabled
assertTrue("button is enabled", mButton2.isEnabled());
//click button
mButton2.performClick();
MainActivity2 MainActivity2 = (MainActivity2) activityMonitor.waitForActivityWithTimeout(TIMEOUT_IN_MS );
assertNotNull("MainActivity2 is null", MainActivity2);
// assertEquals("Monitor for MainActivity2 has not been called", 1, activityMonitor.getHits());
// assertEquals("Activity is of wrong type", MainActivity2.class, MainActivity2.getClass());
// Remove the ActivityMonitor
getInstrumentation().removeMonitor(activityMonitor);
}
// public void tearDown() {
// }
}
(I know it's really simple, but I'm just trying to get the basics down.)
My application.
package com.example.twoactivities;
import com.example.twoactivities.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void activity2(View view){
Intent intent = new Intent(this,com.example.twoactivities.MainActivity2.class);
startActivity(intent);
}
}
Activity 2 of my application.
package com.example.twoactivities;
import com.example.twoactivities.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity2 extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
public void activity1(View view){
Intent intent = new Intent(this,com.example.twoactivities.MainActivity.class);
startActivity(intent);
}
}
Any ideas why my test class doesn't run?
Thanks,
Stephanie
You have to right click on the test itself, not the project.
According to documentation Android testing API supports JUnit 3 code style, but not JUnit 4. Try naming your test method testClick

Can't get android service to work

I'm very new to android. I want to create an application that turns off all sounds at the selected time. I created a service with some code and in Eclipse there's no errors, but when I press the button nothing happens. I can see in Application Manager that my program and the service SilentHours are running. Here's my code:
MainActivity.java
package com.example.silencecontrol;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
public final static String EXTRA_NAME = "sending silentHour value to service SilenceHours";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void setSilenceHours(View view) {
EditText editText1 = (EditText)findViewById(R.id.editText1);
TextView textView1 = (TextView)findViewById(R.id.textView1);
if(editText1.length() > 0) {
Intent intent = new Intent(this, SilentHours.class);
String editText1String = editText1.getText().toString();
int silentHour = Integer.parseInt(editText1String);
intent.putExtra(EXTRA_NAME, silentHour);
this.startService(intent);
} else {
textView1.setText("Please enter the silence hour. ");
}
}
}
SilentHours.java
package com.example.silencecontrol;
import java.util.Calendar;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.IBinder;
public class SilentHours extends Service {
public SilentHours() {
}
protected void onHandleIntent(Intent intent) {
int silentHour = Integer.parseInt(intent.getStringExtra(MainActivity.EXTRA_NAME));
Calendar calendar = Calendar.getInstance();
int currentTime = calendar.get(Calendar.HOUR_OF_DAY);
if(currentTime >= silentHour) {
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
}
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
And by the way I can't #Override the onHandleIntent method. If I put the #Override annotation I get error:
The method onHandleIntent(Intent) of type SilentHours must override or implement a supertype method.
Is that annotation necessary?
The method you are looking for is named onStartCommand, not onHandleIntent.
And no, it's not necessary to add this annotation to an overriden method, it's just a very good practice as you can get sure that you respected the signature of the super class.
Let you IDE help you when you code. Simply type "on" then ask for completion to see which method you can override.

The method startActivity(Intent) is undefined for the type MyWebViewClient

I'm trying to use the Android tutorial to build an app that loads a web view to a mobile site that I built. The problem is with following the tutorial the startActivity function is undefined and the Android tutorial isn't helping. I've done Ctrl+Shift+O to verify all the proper modules are loaded.
package com.mysite;
import android.content.Intent;
import android.net.Uri;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
Update
Ok, now my code reads:
package com.myapp;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MyApp extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init webview
WebView DCWebView = (WebView) findViewById(R.id.webview);
WebSettings webViewSettings = DCWebView.getSettings();
//when a link is clicked, use the WebView instead of opening a new browser
DCWebView.setWebViewClient(new MyWebViewClient() {
#Override
public void launchExternalBrowser(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
});
//enable javascript
webViewSettings.setJavaScriptEnabled(true);
}
}
But I'm showing 2 errors:
Description Resource Path Location Type
The type new MyWebViewClient(){} must implement the inherited abstract method MyWebViewClient.launchExternalBrowser() DealClippings.java /MyApp/src/com/myapp line 21 Java Problem
The method launchExternalBrowser(String) of type new MyWebViewClient(){} must override or implement a supertype method MyApp.java /DealClippings/src/com/myapp line 23 Java Problem
There really is no startActivity method for WebViewClient. You can check the docs. You'll have to signal the Context (probably your Activity) to execute those lines of code instead. There are many possible approaches including adding listeners or simply calling an abstract method which you implement in an anonymous instance of this class when setting the WebViewClient of your WebView in your Activity.
For example:
public abstract class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
launchExternalBrowser(url);
return true;
}
public abstract void launchExternalBrowser(String url);
}
And then in your activity:
WebViewClient client = new MyWebViewClient() {
#Override
public void launchExternalBrowser(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
};
Although I'm not sure why you want this behavior exactly, but it should work more or less.
I don't know if there's much point answering your edit now, 18 months on, but it seems like this question gets a bit of traffic so I'll post this here for posterity.
From your errors, it sounds like you haven't provided an argument to the abstract method in the abstract class definition of MyWebViewClient. That is, you have this:
public abstract void launchExternalBrowser();
when you should have this:
public abstract void launchExternalBrowser(String url);
The cause of the error is that Java treats two methods with the same name but different arguments as two distinct methods. So launchExternalBrowser(String) is a different method to launchExternalBrowser().
Hope this helps someone!
To answer original question before your edit..
Had the same problem, and figured out that the MyWebViewClient is meant to be an inner class inside the activity.
package com.myapp;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MyApp extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init webview
WebView DCWebView = (WebView) findViewById(R.id.webview);
WebSettings webViewSettings = DCWebView.getSettings();
//when a link is clicked, use the WebView instead of opening a new browser
DCWebView.setWebViewClient(new MyWebViewClient());
//enable javascript
webViewSettings.setJavaScriptEnabled(true);
}
private class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}

Categories