Android - Call method from main class within JavaScriptInterface - android

I'm relatively new to android development and I'm trying to make a WebView that will allow me to launch the android camera app. How can I go about calling a method in the main class from with my JavaScriptInterface?
Thanks.
public class MainActivity extends Activity {
public static final int MEDIA_TYPE_IMAGE = 1888;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
mainWebView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
WebSettings webSettings = mainWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mainWebView.setWebViewClient(new MyCustomWebViewClient());
//mainWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mainWebView.loadUrl("file:///mnt/sdcard/page.html");
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
public void takePicture() {
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, MEDIA_TYPE_IMAGE);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MEDIA_TYPE_IMAGE) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
}}
}
package com.cargowise.view;
import android.content.Context;
import android.os.Handler;
import android.widget.Toast;
public class JavaScriptInterface {
Context mContext;
/** Instantiate the interface and set the context */
JavaScriptInterface(Context c) {
mContext = c;
}
public void takePic() {
MainActivity.takePicture();
}
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}

You have stored the context which you should be able to use to access your MainActivity.
Change this:
public void takePic() {
MainActivity.takePicture();
}
To this:
public void takePic() {
((MainActivity)mContext).takePicture();
}
Note: you might want to add some type-checking or limit the type of the context that is given to a MainActivity to enforce correct behavior.

The suggestion of calling ((MainActivity)mContext).takePicture() is (probably) wrong, at least for me it failed. The reason is that the call will be not on the main/UI thread, but rather on some another thread.
Also, you MUST put the #JavascriptInterface annotation before each interface method.
The right way to place calls to main UI thread, which in turn will be able to do everything as usual on that thread, is this:
...inside JavaScriptInterface class
public void takePic() {
((MainActivity)mContext).post(new Runnable() {
#Override
public void run() {
((MainActivity)mContext).takePicture();
}
});
}
In Kotlin it would look much simpler:
#JavascriptInterface
fun takePic() {
(mContext as MainActivity).mainWebView?.post {
(mContext as MainActivity).takePicture()
}
}
Full example of integration of WebView with Android backend, with calls bak and forth between JS and Android, is here: https://github.com/latitov/Android_WebViewApp_FullScreen

Related

Getting thread result in another activity for Android

My Main Activity invokes ResultActivity and at the same time invokes a Runnable thread. I want that Result activity doesn't wait for some part of result processing (Image) which may be provided later by thread and UI may be updated accordingly.
What I've tried to do is given below.
MainActivity (below method is actually a callback from another thread):
#Override
public void onCreate(Context context) {
resultImageProcessor = new ResultImageProcessor();
resImgProThread = new Thread(resultImageProcessor);
}
#Override
public void onBarcodeDetected(final Barcode barcode) {
resultImageProcessor.setCameraBarcode(mCameraSource,barcode);
resImgProThread.start();
Intent intent = new Intent(this, ResultActivity.class);
intent.putExtra(BarcodeObject, barcode);
intent.putExtra(ResultCode, CommonStatusCodes.SUCCESS);
intent.putExtra(ResImgProcObject, resultImageProcessor);
startActivity(intent);
}
Result Image Processor:
public class ResultImageProcessor implements Serializable, Runnable {
private ResultActivity resultActivityContext;
ResultImageProcessor(){
this.resultActivityContext = null;
}
public void setResultActivity(ResultActivity resultActivity) {
this.resultActivityContext = resultActivity;
}
public void setCameraBarcode(CameraSource cameraSource, Barcode barCode){
mCameraSource = cameraSource;
barcode = barCode;
}
#Override
public void run() {
String picPath = ProcessImage(Obj..Attributes);
//wait until result activity context is not set
while(resultActivityContext == null){
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
resultActivityContext.onImageSaved(picPath);
}
}
Result Activity:
protected void onCreate(Bundle savedInstanceState) {
//...
data = getIntent();
Barcode barcode = data.getParcelableExtra(MainActivity.BarcodeObject);
ResultImageProcessor resultImageProcessor = data.getParcelableExtra(MainActivity.ResImgProcObject);
resultImageProcessor.setResultActivity(this);
}
//called from Result image processor
public void onImageSaved(String imagePath){
ImageView barImgView = findViewById(R.id.barcode_image);
Bitmap barcodeImage = BitmapFactory.decodeFile(imagePath);
barImgView.setImageBitmap(barcodeImage);
barImgView.invalidate();
}
With the above code, after invoking resultImageProcessor.startProcessing(), result activity is not launched nor runnable's run() method keeps busy in while loop. I traced them using logger. When I skip threading and pass image path to activity, everything goes fine beside being slow for activity switching.
Please indicate the problem or suggest better solution.
i think you are missing run in your runnable like this
new Runnable() {
#Override
public void run() {
String picPath = ProcessImage(Obj..Attributes);
//wait until result activity context is not set
while(resultActivityContext == null){}
resultActivityContext.onImageSaved(picPath);
}
}.run();
It turned out that the problem was in passing ResultImageProcessor object to ResultActivity intent as Parcelable. I followed a simple path of declaring resultActivityContext as static in ResultImageProcessor.
public class ResultImageProcessor implements Runnable {
public static ResultActivity resultActivityContext;
...
#Override
public void run() {
...
resultActivityContext.onImageSaved(picPath);
resultActivityContext = null;
}
}
and in ResultActivity:
ResultImageProcessor.resultActivityContext = this;

Cordova phonegap android application does not exit

I am New in phonegap cordova development.i make an demo apllication with help of google in cordova.here i take a button named exitapp and called function navigator.app.exitApp() function.here it is working fine but if I add some methods in mainActivity,java file then navigator.app.exitApp() function is not working.
I used
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
in html page.
my activity code is.
package com.example.hello;
import com.acl.paychamp.util.DecryptData;
import com.example.hello.R;
public class MainActivity extends CordovaActivity implements
CordovaInterface
{
CordovaWebView cwv;
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private CordovaPlugin activityResultCallback;
public static final String PASSKEY = "s407iejl";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
setContentView(R.layout.main);
cwv = (CordovaWebView) findViewById(R.id.view);
cwv.loadUrl(this.launchUrl);
}
#Override
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
this.activityResultCallback = command;
// Start activity
super.startActivityForResult(intent, requestCode);
}
#Override
public void setActivityResultCallback(CordovaPlugin plugin) {
this.activityResultCallback = plugin;
}
#Override
public Activity getActivity() {
return this;
}
#Override
public Object onMessage(String id, Object data) {
try {
URI uri = new URI(data.toString());
if (uri.toString().contains("http://abcd.com/pr?
param=")) {
String encryptedText = uri.getQuery().split("=")[1];
JSONObject jObj = DecryptData.getDecryptedJson(encryptedText,
PASSKEY);
Log.i("CordovaApp", jObj.get("msg").toString());
Log.i("CordovaApp", jObj.get("trxid").toString());
cwv.loadUrl("file:///android_asset/www/status.htm?
resparam="+encryptedText);
}
} catch (Exception use) {
}
return null;
}
#Override
public ExecutorService getThreadPool() {
return threadPool;
}
#Override
protected void onActivityResult(final int requestCode, final int
resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
CordovaPlugin callback = this.activityResultCallback;
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, intent);
}
}
}
In case you mean by minimizing that app that the application goes to the background (is not the active application but it is still in the list of running applications). In case that is what you mean, please notice that this is the normal behavior in Android and many Mobile OS.
Terminating (exiting if you prefer) the application is not under the control of the application developer. It is completely managed by the OS (Android in this case). So, what you have is the normal behavior for apps under Mobile OS.
SOURCE : navigator.app.exitapp() not working in android device

Android: unable to lauch new Activity from an inner class

Android: someone help:
I notice this kind of question has been asked before by other people but the answers have not been useful to my my case; I need to launch a new activity from an inner
class but all I get is the error bellow:
04-05 15:00:43.851: E/AndroidRuntime(3288): Caused by: java.lang.InstantiationException: com.school.School$StudentProfile
Here is my code snippet:
public class School extends Activity{
ProgressDialogue progressDialogue;
protected WebViewTask _webTask;
String path = "http://www.school.com/student/";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.school);
progressDialogue = new ProgressDialogue(School.this);
_webTask = new WebViewTask();
_webTask.execute();
}
//rest of the code
/** The inner class */
public class StudentProfile {
Context context;
/** Instantiate the interface and set the context */
public StudentProfile(Context c) {
context=c;
}
/** launch student activity */
public void lauchProfile() {
School.this.startActivity(new Intent(School.this, StudentProfile.class));
//Intent intent = new Intent(School.this, StudentProfile.class);
//startActivity(intent);
}
}
void webView(){
String url = path +"student.php";
WebView wv = (WebView) findViewById(R.id.trivia_webview);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
wv.addJavascriptInterface(new StudentProfile (this), "Student");
wv.loadUrl(url);
wv.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// open URL in the web view itself
if (url.contains(url))
return super.shouldOverrideUrlLoading(view, url);
// open URL in an external web browser
else {
return true;
}
}
});
}
// rest of the code
NOTE: there is a 'student' button on the web view that is supposed to launch the StudentProfile activity.
Your StudentProfile is not an Activity, so you can not start it that way. It needs to be a separate class, and declared in AndroidManifest.xml.

What is the context parameter for ProgressDialog.show()?

I am new to android.
I am trying to build a simple android application: User clicks the button and a progress dialog appears for 5 seconds.
I used ProgressDialog.show() and got a problem with the context parameter.
Here is my xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/btnDialog2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/btnDialog2" />
</LinearLayout>
And here is my code:
public class Dialog22Activity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnDialog2 = (Button)findViewById(R.id.btnDialog2);
btnDialog2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final ProgressDialog dialog = ProgressDialog.show(getBaseContext(),
"Progress dialog", "Loading...", true);
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
dialog.dismiss();
} catch (InterruptedException e) {
}
}
}).start();
}
});
}
}
If i change the context parameter of ProgressDialog.show() from getBaseContext() to v.getContext() my program run normally.
So I wanna ask what is the meanning of context parameter here?
Thanks for your helps.
Just use Dialog22Activity.this instead of getContext() or WhatEverContextFunction() you want, when ever you are within this class and you'll be cool :)
You can refer the android docs for these explanations please see this
http://developer.android.com/reference/android/content/Context.html
getContext() is not defined in an Activity. It's used in a View (or View subclass) to get a reference to the enclosing context (an Activity).
The context in ProgressDialog.show(context) refers to the parent context of this ProgressDialog.
BaseContext effectively returns which ever context is being wrapped by ContextWrapper.
By looking at the code, I can say that this is likely an Activity or Application however ContextWrapper has over 40 known direct and indirect children.
The problem is that this means that what the method returns may be ambiguous and I would rather use getContext() or the Activity, FragmentActivity, ActionBarActivity etc. directly, so that I know what I’m holding on to and that I’m holding a reference to something that can cause a memory leak. Also, I have the additional benefit of being able to take advantage of methods that are provided by these classes.
In case you want to use a ProgressDialog in an external class to an activity, you would create an instance of a Context inside this class and ProgressDialog would use its context:
public class DownloadTask extends AsyncTask<URL, Integer, Void>{
ProgressDialog progressDialog;
private Context context;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(context);
}
If you are showing the progress dialog on current Activity, use classname.class or 'this' keyword for context.
import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.webkit.WebView;
import android.view.View;
import android.webkit.WebViewClient;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.JsResult;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private WebView webView;
private ProgressDialog progressDialog;
startWebView("http://.../mobile/");
}
private void startWebView(String url) {
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
webView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("Loading...");
progressDialog.setMessage("Wait while loading...");
progressDialog.setCancelable(false); // disable dismiss by tapping outside of the dialog
progressDialog.show();
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(getApplicationContext(), "Error:" + description, Toast.LENGTH_SHORT).show();
}
});
webView.loadUrl(url);
}
}
its the activity context. use:
public class Dialog22Activity extends Activity {
private Activity activity;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
activity = this;
Button btnDialog2 = (Button)findViewById(R.id.btnDialog2);
btnDialog2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final ProgressDialog dialog = ProgressDialog.show(activity,
"Progress dialog", "Loading...", true);
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
dialog.dismiss();
} catch (InterruptedException e) {
}
}
}).start();
}
});
}
}
information about context: What is 'Context' on Android?

Progress bar with AsyncTask Object on initial app load

I have looked around on the API and through a few questions on here, and I think I am on the right path. My app is based on a webView object and the initial load has quite a few cached pages so I want progressDialog on the initial start up instead of the blank black screen. Right now the app just crashes but I believe it is because I am creating and calling the AsyncTask object in the wrong place. Right now it is being called in the onCreate() method. I’m not new to Java but I am new to Android and this idea of not working with a main() function is confusing to me.
So where should I call the execute() function if I only want the ProgressDialog shown on the initial launch? And is my AsyncTask object even set up correctly?
public class site extends Activity {
private WebView engine;
private String urlSave;
private WebViewClient yourWebClient;
private ProgressDialog initLoadDialog;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
yourWebClient = new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("tel:") == true) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
startActivity(intent);
}
else if(url.contains(“blah") == true && url.contains(“blah2") == false) {
view.loadUrl(url);
}
else if(url.contains(“blah3") == true) {
double[] loc = getGPS();
url += "&cLat=" + loc[0] + "&cLong=" + loc[1];
view.loadUrl(url);
}
else {
/*Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("url"));
startActivity(browserIntent);*/
}
return true;
}
};
}
#Override
public void onStart() {
progressSetup();
setContentView(R.layout.main);
}
public void progressSetup () {
initLoadDialog = new ProgressDialog(site.this);
initLoadDialog.setMessage("A message");
initLoadDialog.setIndeterminate(false);
initLoadDialog.setMax(100);
initLoadDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
urlLoad loading = new urlLoad();
loading.execute();
}
private class urlLoad extends AsyncTask<String, Integer, String>{
#Override
protected String doInBackground(String... url) {
try {
engine = (WebView) findViewById(R.id.web_engine);
engine.getSettings().setJavaScriptEnabled(true);
engine.getSettings().setBuiltInZoomControls(true);
engine.getSettings().setSupportZoom(true);
engine.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
engine.getSettings().setGeolocationEnabled(true);
engine.setWebViewClient(yourWebClient);
engine.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
engine.loadUrl(“albhal");
} catch (Exception e) {}
return null;
}
protected void onProgressUpdate(Integer... progress) {
initLoadDialog.setProgress(engine.getProgress());
}
}
}
Check your adb log, the error will pretty much explain to you what you didn't do right.
There's a lot of bad practice in your code. For example you call setContentView() in two Methods with different Layouts. The Flow of a android application is to call "onCreate", then "onStart". There is no reason to distinguish between those methods for you. Merge them and decide which layout to populate.
Also it is recommended to change the user-interface (this means also the dialogs) through the managing activity. In your case you are creating a ProgressDialog in the activity which then gets modified by the task. This is something you should avoid.

Categories

Resources