I need to collect browser cookies from a WebView and return them to another Activity. Collecting the cookies works already but happens within a class that extends WebViewClient, and I don't know how to return the data from there.
There are 2 Activities; MainActivity and LoginActivity, respectively. MainActivity has a single button that creates an Intent and starts LoginActivity, which has a single WebView. OnCreate(), the WebView is directed to the login page of a website. When the user successfully logs into this site, it stores data in the browser cookies - data that I need for an API call later. How can I return this data to MainActivity?
The WebView has a custom WebViewClient with an overridden OnPageFinished() method, which if the URL is the one expected, will collect the browser cookies, as shown below.
class TempWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
// If page URL is the Home page (ie., you logged in successfully), collect cookies.
if (url.equals("https://slate.sheridancollege.ca/d2l/m/home")) {
String cookies = CookieManager.getInstance().getCookie(url);
String[] cookies2 = cookies.split(";");
String key1 = "";
String key2 = "";
// Find Keys placed in browser cookies.
for (String c : cookies2) {
String[] c2 = c.split("=");
if (c2[0].equals(" phrase1")) {
key1 = c2[1];
}
if (c2[0].equals(" phrase2")) {
key = c2[1];
}
}
}
}
}
LoginActivity should not call finish() (returning to MainActivity) until I've received the keys from the cookies. How can I achieve this, given what I have already? I don't know how LoginActivity can hold off returning to MainActivity until it has the data from its WebView.
public class LoginActivty extends Activity {
WebView webview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setupViews();
setContentView(webview);
}
public void setupViews() {
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
CookieManager.getInstance().setAcceptCookie(true);
webview = new WebView(this);
webview.setWebViewClient(new TempWebViewClient());
webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl("website");
}
}
Create reference of login activity in TempWebclient to pass the result once you get your cookie.
class TempWebViewClient extends WebViewClient {
LoginActivty mDelegate;
public TempWebViewClient(LoginActivty ref) {
this.mDelegate = ref;
}
#Override
public void onPageFinished(WebView view, String url) {
// perform task
String cookie;// get from CookieManager
mDelegate.sendCookieAndFinish(cookie);
}
}
Create function in your login activity
public class LoginActivty extends Activity {
WebView webview;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
}
public void setupViews() {
....
// send activity reference to webclient
webview.setWebViewClient(new TempWebViewClient(this));
}
public void sendCookieAndFinish(String cookie){
setResult(ResultCode,ResultIntent); // set result for your main activity
// store cookie to storage
finish();
}
}
From Main Activity start your LoginActivity for result
Related
I feel like the answer should be obvious but I can't find it. How do I reference a WebView in my main activity from Application class? Using a public static var to hold a reference to it in Activity class is causing a memory leak.
Current code (works, but causes memory leak).
Activity
public class Core extends AppCompatActivity {
public static WebView coreView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_core);
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
myWebView.setWebViewClient(new WebViewClient());
myWebView.setPadding(0,80,0,0);
coreView = myWebView;
}
}
Application
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
Core.coreView.loadUrl("http://www.google.com");
}
EDIT:
Full App class.
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
OneSignal.startInit(this)
.inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
.setNotificationOpenedHandler(new MainNotificationOpenedHandler())
.setNotificationReceivedHandler(new MainNotificationReceivedHandler())
.unsubscribeWhenNotificationsAreDisabled(true)
.init();
OSPermissionSubscriptionState status = OneSignal.getPermissionSubscriptionState();
}
private class MainNotificationOpenedHandler implements OneSignal.NotificationOpenedHandler {
// This fires when a notification is opened by tapping on it.
#Override
public void notificationOpened(OSNotificationOpenResult result) {
OSNotificationAction.ActionType actionType = result.action.type;
JSONObject data = result.notification.payload.additionalData;
String customKey;
Core.coreView.loadUrl(result.notification.payload.launchURL);
}
}
private class MainNotificationReceivedHandler implements OneSignal.NotificationReceivedHandler {
#Override
public void notificationReceived(OSNotification notification) {
JSONObject data = notification.payload.additionalData;
}
}
}
How do I reference a WebView in my main activity from Application class?
You don't. You move that code into the activity, or something scoped to that activity (e.g., a fragment).
For example, your code in your question is guaranteed to crash, as Core.coreView will not be set by the time onCreate() of an Application is called. onCreate() of an Application is called before any components, such as an activity, are created, as part of your process starting.
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.
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
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.
I'm sharing some variables accross activities by using a class like this :
public class Globals {
static Boolean hint1_graph_type_switcher;
static Boolean hint2_stockview_valuation;
other variables ...
}
then I'm using these variables anywhere across my multiple activites with ...
if (Globals.hint2_stockview_valuation == false) {
....
}
pretty basic and it was working fine untill ...
I introduced some webview stuff like this:
//-----------------------------------------------------------
// open a webview with the NEWS when the more_arrow is clicked :
mNews.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String news_URL = "http://us.m.yahoo.com/w/yfinance/symbolheadlines/"+ ticker + "/?.intl=us&.lang=en";
Intent news_Webview_intent = new Intent(Chart_View.this, News_Webview.class);
news_Webview_intent.putExtra("NEWS_URL", news_URL);
startActivity(news_Webview_intent);
}
});
//-----------------------------------------------------------
and here's the News_Webview.class:
public class News_Webview extends Activity {
//
// http://www.chrisdanielson.com/tag/progressdialog/
//
String news_URL;
private WebView webview;
private ProgressDialog progressBar;
private static final String TAG = "Hub";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.webview_news);
this.webview = (WebView)findViewById(R.id.webView);
Bundle extras = getIntent().getExtras();
if (this.getIntent().getExtras()!=null){
news_URL = extras.getString("NEWS_URL");
}
WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
webview.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
progressBar = ProgressDialog.show(News_Webview.this, "", "Loading...");
webview.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//Log.i(TAG, "Processing webview url click...");
Intent viewIntent = new Intent("android.intent.action.VIEW", Uri.parse(url));
startActivity(viewIntent);
return true;
}
public void onPageFinished(WebView view, String url) {
//Log.i(TAG, "Finished loading URL: " +url);
if (progressBar.isShowing()) {
progressBar.dismiss();
}
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Log.e(TAG, "Error: " + description);
Toast.makeText(News_Webview.this, "Oh no! " + description, Toast.LENGTH_SHORT).show();
alertDialog.setTitle("Error");
alertDialog.setMessage(description);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
alertDialog.show();
}
});
webview.loadUrl(news_URL);
}
}
the problem now is that, when it "comes back" from this Activity, it looks like my Globals variables have disappeared ???
if (Globals.hint2_stockview_valuation == false) {
fires an error :
06-23 12:14:03.443:
ERROR/AndroidRuntime(2611): Caused by:
java.lang.NullPointerException
2 questions then :
Should I use something else than this "Global" class to share variables across activities ? Is it just bad practice to do this ?? I know that I can use the preferences but I thought it was quicker to do it this way (no need to "read" the preferences everytime I start a new activity ...
Any idea on WHY this is happening ? Should I "get back" my savedInstanceState in some way when my activity returns from the News_Webview.class ???
As always, thank you for your help.
H.
U could use intent.putExtra() for inter-activity interaction...
One thing(just for diagnostics) i would suggest is initializing the static variables to some value and then run the App... i suppose your global class is getting re-initialized when the intent started activity returns back...
I have used global variables, but in my case i kept them in an activity which never died. All other activities came after it and it worked perfectly fine...
The NullPointerException is bubbling up from the android runtime and has nothing to do with your Globals type.