I have an activity (AuthenticationActivity) on Android with an interface. Another class implements this interface. I need to parse an interface object for AuthenticationActivity so when one event happens, another class performs an action.
public class MyWebViewActivity extends Activity {
private WebView mWebView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_webview);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.setWebViewClient(new OAuthWebViewClient());
mWebView.setVerticalScrollBarEnabled(false);
mWebView.setHorizontalScrollBarEnabled(false);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("https://www....");
}
private class OAuthWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(App.callbackUrl)) {
String urls[] = url.split("=");
// listener.onComplete(urls[1]); // HERE I NEED FOR LISTNER
return true;
}
return false;
}
}
public interface OAuthDialogListener {
public abstract void onComplete(String accessToken);
public abstract void onError(String error);
}
}
I have in other class:
OAuthDialogListener listener = new OAuthDialogListener() {
#Override
public void onComplete(String code) {
getAccessToken(code);
}
#Override
public void onError(String error) {
mListener.onFail("Authorization failed");
}
};
ann this class call:
Intent intent = new Intent(this, MyWebViewActivity.class);
startActivity(intent);
how the MyWebViewActivity clas uses listener?
inside OAuthWebViewClient class initialize the OAuthDialogListener and them implement it with MyWebViewActivity
try
public class MyWebViewActivity extends Activity implements OAuthWebViewClient {
Related
I develop an app on Xamarin. I try to hide image when webview page loaded. I try different methods like call public function or access imageview from another class.
I read This but it doesn't work on Xamarin.
So I try something that
[Activity(Label = "XamarinWebView", Theme = "#android:style/Theme.Black.NoTitleBar", MainLauncher = true)]
public class MainActivity : Activity
{
WebView app_view = null;
WebSettings app_web_settings = null;
WebChromeClient web_client;
MyWebViewClient my_web_client;
ImageView my_splash = null;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
app_view = FindViewById(Resource.Id.webViewapp) as WebView;
my_web_client = new MyWebViewClient(this.ApplicationContext);
app_view.SetWebViewClient(my_web_client);
string app_url = "file:///android_asset/app_pages/test.html";
app_view.LoadUrl(app_url);
my_splash = FindViewById(Resource.Id.imageSplash) as ImageView;
my_splash.SetImageDrawable(GetDrawable(Resource.Drawable.splash));
}
public void HideSplash()
{
my_splash.Visibility = ViewStates.Gone;
}
}
In this class I can get WebView page loading status.
public class MyWebViewClient : WebViewClient
{
Context context;
public MyWebViewClient(Context _context) {
this.context = _context;
}
public override void OnPageStarted(WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
**I need to change visible my_splash**
}
}
With Jason's advice I try something that and it works.
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
.
.
.
my_web_client = new MyWebViewClient(this);
.
.
.
}
public void HideSplash()
{
my_splash.Visibility = ViewStates.Gone;
}
}
public class MyWebViewClient : WebViewClient
{
MainActivity act;
public MyWebViewClient(MainActivity activity)
{
this.act = activity;
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
act.Hide_Splash();
}
}
A much easier approach to getting the activity from another class, which is also less error-prone and less likely to cause memory leaks would be to get the activity from the view context.
In your MyWebViewClient class:
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
(view.Context as MainActivity).HideSplash();
}
Or if you're not sure if the context will always be MainActivity you could use pattern matching which covers the null check:
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
if(view.Context is MainActivity mainActivity)
{
mainActivity.HideSplash();
}
}
Using an approach like this is much easier to maintain down the road.
I am loading an Webview url inside my Javascript interface In Android , but they are not working;
How to To handle IT
#SuppressLint({ "SetJavaScriptEnabled", "NewApi" }) public class MainActivity extends ActionBarActivity {
WebView Browser;
String website ="http://192.168.1.4/Soft/Bigcats/Ba/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Browser=(WebView) findViewById(R.id.webView1) ;
class MyJavaScriptInterface
{
#JavascriptInterface
#SuppressWarnings("unused")
public void Danyial(String Data)
{
Browser.loadUrl("http://enjoybaba.com");
}
}
Browser.addJavascriptInterface(new MyJavaScriptInterface(), "api");
Browser.loadUrl(website);
Browser.setWebViewClient(new WebViewClient());
}
}
JavaScript runs in another thread, not UI so you may need to run your code on the UI thread for it to work.
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Browser.loadUrl("http://enjoybaba.com");
}
});
You have Browser.setWebViewClient(new WebViewClient()); after Browser.loadUrl(website);. Try to setWebViewClient before loadUrl.
I would do this a little bit differently. Take a look.
Declare JsHelper.
public interface JsHelper {
public void loadNewUrl();
}
Javascript Interface calls JsHelper
public class JsApi {
JsHelper mJsHelper;
public JsApi(JsHelper jsHelper) {
mJsHelper= mJsHelper;
}
#JavascriptInterface
public void Danyial() {
mJsHelper.loadNewUrl();
}
}
MainActivity implements JsInterface
public class MainActivity extends ActionBarActivity implements JsHelper {
WebView Browser;
String website ="http://192.168.1.4/Soft/Bigcats/Ba/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Browser=(WebView) findViewById(R.id.webView1) ;
Browser.addJavascriptInterface(new JsApi (this), "api");
Browser.setWebViewClient(new WebViewClient());
Browser.loadUrl(website);
}
#Override
public void loadNewUrl(){
Browser.loadUrl("http://enjoybaba.com");
}
}
I have an Activity named MyActivity. MyActivity uses WebView to show web pages.
I also have an options activity like below. O also have /res/xml/options.xml
When user changes something in the options, I run MyUtility.setOptionsChanged() method.
My problem is when user changes something in preferences XML. When user presses back button I need to reload the current webview URL with post data ?
Can you check the code below and where should I put that logic ? Should I add to MyOptionsActivity's onKeyDown method or ?
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
mContext = getApplicationContext();
super.onCreate(savedInstanceState);
setContentView(R.layout.myweb);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.setWebViewClient(new myWebViewClient());
String postData = MyUtility.getOptionsDataForPOSTURL(mContext);
mWebView.postUrl("http://example.com", EncodingUtils.getBytes(postData, "BASE64"));
}
public class myWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
String postData = MyUtility.getOptionsDataForPOSTURL(mContext);
view.postUrl(url, EncodingUtils.getBytes(postData, "BASE64"));
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if(mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
public class MyOptionsActivity extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
Context mContext;
#Override
public void onCreate(Bundle savedInstanceState) {
mContext = getApplicationContext();
super.onCreate(savedInstanceState);
getPreferenceManager().setSharedPreferencesName("myoptions");
addPreferencesFromResource(R.xml.options);
}
#Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
MyUtility.setOptionsChanged(mContext,true);
}
}
public class MyUtility {
public static void setOptionsChanged(Context pContext, Boolean pOptionsChanged) {
SharedPreferences prefs = pContext.getSharedPreferences("general", 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("optionsChanged", pOptionsChanged);
editor.commit();
}
}
Regarding to #Hiren Dabhi's comment, best place is Activity's onRestart method. It worked good.
I have a real big problem implementing loading a intent from inside shouldOverrideUrlLoading.
I believe it has to do with the way i have structured my application.
Here is my code:
VariablesStorage.class
public class VariablesStorage
{
private static VariablesStorage instance;
public static Context webViewContext;
public WebView webView;
public static void initInstance()
{
if (instance == null)
{
// Create the instance
instance = new VariablesStorage();
}
}
public static VariablesStorage getInstance()
{
// Return the instance
return instance;
}
private VariablesStorage()
{
}
public void loadWebView() {
webView.getSettings().setJavaScriptEnabled(true);
if (isOnline())
{
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("inapp://")) {
Intent intent = new Intent(this,profilepictureview.class);
intent.putExtra("img",Uri.parse(url).getHost().toString());
startActivity(intent);
}else
{
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
}
return true;
}
#Override
public void onReceivedError( WebView view, int errorCode, String description, String failingUrl)
{
}
#Override
public void onPageFinished(WebView view, String url) {
if(mProgress.isShowing()) {
mProgress.dismiss();
}
}
});
webView.loadUrl(Url);
}else
{
webView.loadData(customHtml, "text/html;charset=utf-8", "UTF-8");
}
}
}
and my webview activity where i call VariablesStorage.getInstance().loadWebView();
WebViewActivity.class
public class WebViewActivity extends Activity {
SharedPreferences pref;
TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webviewtab);
VariablesStorage.getInstance().webViewContext = this;
VariablesStorage.getInstance().webView = (WebView) findViewById(R.id.webView);
VariablesStorage.getInstance().loadWebView();
}
So when i am adding this code inside shouldOverrideUrlLoading
Intent intent = new Intent(this,profilepictureview.class);
intent.putExtra("img",Uri.parse(url).getHost().toString());
startActivity(intent);
i am getting this error:
The constructor Intent(new WebViewClient(){}, Class<profilepictureview>) is undefined
Any help implementing this one?
Thanks.
As the error says, Intent doesn't have a constructor that takes WebViewClient(){} as a parameter which is what this is referring to in this situation. See Intent Constructors
What you probably want is
Intent intent = new Intent(webViewContext,profilepictureview.class);
but you will want to initialize webViewContext first.
Edit
startActivity also, needs a Context. Try this
webViewContext.startActivity(intent);
Keeping a static reference to an activity's context is going to cause you problems. All views (including WebView) have getContext(), so use view.getContext() rather than 'this' or 'webViewContext' when creating the Intent.
Also, you're trying to start an activity named 'profilepictureview', which sounds more like a View rather than an Activity?
I want to get a return value from Javascript in Android. I can do it with the iPhone, but I can't with Android. I used loadUrl, but it returned void instead of an object. Can anybody help me?
Same as Keith but shorter answer
webView.addJavascriptInterface(this, "android");
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)");
And implement the function
#JavascriptInterface
public void onData(String value) {
//.. do something with the data
}
Don't forget to remove the onData from proguard list (if you have enabled proguard)
Here's a hack on how you can accomplish it:
Add this Client to your WebView:
final class MyWebChromeClient extends WebChromeClient {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
Now in your javascript call do:
webView.loadUrl("javascript:alert(functionThatReturnsSomething)");
Now in the onJsAlert call "message" will contain the returned value.
Use addJavascriptInterface() to add a Java object to the Javascript environment. Have your Javascript call a method on that Java object to supply its "return value".
Here's what I came up with today. It's thread-safe, reasonably efficient, and allows for synchronous Javascript execution from Java for an Android WebView.
Works in Android 2.2 and up. (Requires commons-lang because I need my code snippets passed to eval() as a Javascript string. You could remove this dependency by wrapping the code not in quotation marks, but in function(){})
First, add this to your Javascript file:
function evalJsForAndroid(evalJs_index, jsString) {
var evalJs_result = "";
try {
evalJs_result = ""+eval(jsString);
} catch (e) {
console.log(e);
}
androidInterface.processReturnValue(evalJs_index, evalJs_result);
}
Then, add this to your Android activity:
private Handler handler = new Handler();
private final AtomicInteger evalJsIndex = new AtomicInteger(0);
private final Map<Integer, String> jsReturnValues = new HashMap<Integer, String>();
private final Object jsReturnValueLock = new Object();
private WebView webView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = (WebView) findViewById(R.id.webView);
webView.addJavascriptInterface(new MyJavascriptInterface(this), "androidInterface");
}
public String evalJs(final String js) {
final int index = evalJsIndex.incrementAndGet();
handler.post(new Runnable() {
public void run() {
webView.loadUrl("javascript:evalJsForAndroid(" + index + ", " +
"\"" + StringEscapeUtils.escapeEcmaScript(js) + "\")");
}
});
return waitForJsReturnValue(index, 10000);
}
private String waitForJsReturnValue(int index, int waitMs) {
long start = System.currentTimeMillis();
while (true) {
long elapsed = System.currentTimeMillis() - start;
if (elapsed > waitMs)
break;
synchronized (jsReturnValueLock) {
String value = jsReturnValues.remove(index);
if (value != null)
return value;
long toWait = waitMs - (System.currentTimeMillis() - start);
if (toWait > 0)
try {
jsReturnValueLock.wait(toWait);
} catch (InterruptedException e) {
break;
}
else
break;
}
}
Log.e("MyActivity", "Giving up; waited " + (waitMs/1000) + "sec for return value " + index);
return "";
}
private void processJsReturnValue(int index, String value) {
synchronized (jsReturnValueLock) {
jsReturnValues.put(index, value);
jsReturnValueLock.notifyAll();
}
}
private static class MyJavascriptInterface {
private MyActivity activity;
public MyJavascriptInterface(MyActivity activity) {
this.activity = activity;
}
// this annotation is required in Jelly Bean and later:
#JavascriptInterface
public void processReturnValue(int index, String value) {
activity.processJsReturnValue(index, value);
}
}
On API 19+, the best way to do this is to call evaluateJavascript on your WebView:
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
#Override public void onReceiveValue(String value) {
// value is the result returned by the Javascript as JSON
}
});
Related answer with more detail: https://stackoverflow.com/a/20377857
The solution that #Felix Khazin suggested works, but there is one key point missing.
The javascript call should be made after the web page in the WebView is loaded. Add this WebViewClient to the WebView, along with the WebChromeClient.
Full Example:
#Override
public void onCreate(Bundle savedInstanceState) {
...
WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new MyWebViewClient());
webView.setWebChromeClient(new MyWebChromeClient());
webView.loadUrl("http://example.com");
}
private class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished (WebView view, String url){
view.loadUrl("javascript:alert(functionThatReturnsSomething())");
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
private class MyWebChromeClient extends WebChromeClient {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
As an alternative variant that uses a custom scheme to communicate Android native code <-> HTML/JS code. for example MRAID uses this technic[About]
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
final WebView webview = new CustomWebView(this);
setContentView(webview);
webview.loadUrl("file:///android_asset/customPage.html");
webview.postDelayed(new Runnable() {
#Override
public void run() {
//Android -> JS
webview.loadUrl("javascript:showToast()");
}
}, 1000);
}
}
CustomWebView
public class CustomWebView extends WebView {
public CustomWebView(Context context) {
super(context);
setup();
}
#SuppressLint("SetJavaScriptEnabled")
private void setup() {
setWebViewClient(new AdWebViewClient());
getSettings().setJavaScriptEnabled(true);
}
private class AdWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("customschema://")) {
//parse uri
Toast.makeText(CustomWebView.this.getContext(), "event was received", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}
}
customPage.html (located in the assets folded)
<!DOCTYPE html>
<html>
<head>
<title>JavaScript View</title>
<script type="text/javascript">
<!--JS -> Android-->
function showToast() {
window.location = "customschema://goto/";
}
</script>
</head>
<body>
</body>
</html>
You can do it like this:
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public WebView web_view;
public static TextView textView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Window.AddFlags(WindowManagerFlags.Fullscreen);
Window.ClearFlags(WindowManagerFlags.ForceNotFullscreen);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.main);
web_view = FindViewById<WebView>(Resource.Id.webView);
textView = FindViewById<TextView>(Resource.Id.textView);
web_view.Settings.JavaScriptEnabled = true;
web_view.SetWebViewClient(new SMOSWebViewClient());
web_view.LoadUrl("https://stns.egyptair.com");
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class SMOSWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return false;
}
public override void OnPageFinished(WebView view, string url)
{
view.EvaluateJavascript("document.getElementsByClassName('notf')[0].innerHTML;", new JavascriptResult());
}
}
public class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string Result;
public void OnReceiveValue(Java.Lang.Object result)
{
string json = ((Java.Lang.String)result).ToString();
Result = json;
MainActivity.textView.Text = Result.Replace("\"", string.Empty);
}
}