I've been following the documentation for the webview2 on microsoft's official website but I have encountered a problem that I am not sure how to fix.
I have added a .NET object using AddHostObjectToScript and it works as long as the function has no parameter. When calling the object function that has a parameter in JS, I keep on getting a "parameter is incorrect" error.
This is how I am calling the host objects in angular app:
result = await window?.chrome?.webview?.hostObjects.bridge.Func("John");
and this is from my WinUI 3.0 app:
[ComVisible(true)]
public class Bridge
{
public string Func(string param)
{
return "Example: " + param;
}
public string Sample()
{
return "Example: ";
}
public BridgeAnotherClass AnotherObject { get; set; } = new BridgeAnotherClass();
// Sample indexed property.
[System.Runtime.CompilerServices.IndexerName("Items")]
public string this[int index]
{
get { return m_dictionary[index]; }
set { m_dictionary[index] = value; }
}
private Dictionary<int, string> m_dictionary = new Dictionary<int, string>();
}
public sealed partial class WebViewPage : Page
{
public WebViewViewModel ViewModel { get; }
public WebViewPage()
{
ViewModel = Ioc.Default.GetService<WebViewViewModel>();
InitializeComponent();
ViewModel.WebViewService.Initialize(webView);
webView.WebMessageReceived += getMsg;
InitializeAsync();
}
async void InitializeAsync()
{
await webView.EnsureCoreWebView2Async();
var interop = webView.CoreWebView2.As<ICoreWebView2Interop>();
interop.AddHostObjectToScript("bridge", new Bridge());
}
WebView2 currently has an issue where the WinRT API's interop interface AddHostObjectToScript doesn't work well with .NET objects. This is a bug in WebView2.
Related
I get the json value using the retrofit library at application launch and I want to send it to the global variable in the class. How can I do it?
Domain is coming, I can see it on the screen with toast message
public void onCreate() {
setRetrofitSettings();
}
public void setRetrofitSettings(){
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
timeApi = retrofit.create(TimeApi.class);
timeTurkeyCall = timeApi.getTime();
timeTurkeyCall.enqueue(new Callback<TimeTurkey>() {
#Override
public void onResponse(Call<TimeTurkey> call, Response<TimeTurkey> response) {
if (response.isSuccessful()){
timeTurkey = response.body();
Global.APIURL = String.valueOf(timeTurkey.getDateTime());
// I want to send the value here.
}
}
#Override
public void onFailure(Call<TimeTurkey> call, Throwable t) {
System.out.println(t.toString());
}
});
}
I want to send post value to global class
I want the incoming data to be assigned to the API_URL variable from here
public class Global {
public static final String API_URL;
}
I want the incoming domain to be active as long as the application is open.
I want to implement a mechanism in a custom webview client (without JavaScript injection) that can block ads. Is a way I can catch ads and replace them with other ads from a trusted source?
Thanks
In your custom WebViewClient, you can override the function shouldInterceptRequest(WebView, WebResourceRequest).
From Android docs:
Notify the host application of a resource request and allow the application to return the data.
So the general idea is to check if the request is coming from an ad URL (plenty of black list filters out there), then return a "fake" resource that isn't the ad.
For a more in depth explanation plus an example, I recommend checking out this blog post.
To implement this, you have two options:
Use Javascript injected code to do this (which you explicitely said, don't want)
In WebView, instead of "http://example.com" load "http://myproxy.com?t=http://example.com" (properly escaped, of course) and setup "myproxy.com" to be a proxy which will fetch the upstream page (given in "t" query parameter, or in any other way) and replace ads with the trusted ones before sending response to the client. This will be pretty complex, though, because ads can be in many forms, they're usually Javascript injected themselves and you'd probably need to rewrite a lot of URL's in the fetched HTML, CSS and JS files etc.
I made a custom WebViewClient like:
public class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) { }
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".mp4")) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), "video/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
return true;
} else if (url.startsWith("tel:") || url.startsWith("sms:") || url.startsWith("smsto:")
|| url.startsWith("mms:") || url.startsWith("mmsto:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
private Map<String, Boolean> loadedUrls = new HashMap<>();
#SuppressWarnings("deprecation")
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
boolean ad;
if (!loadedUrls.containsKey(url)) {
ad = AdBlocker.isAd(url);
loadedUrls.put(url, ad);
} else {
ad = loadedUrls.get(url);
}
return ad ? AdBlocker.createEmptyResource() :
super.shouldInterceptRequest(view, url);
}
}
And created an AdBlocker class like:
public class AdBlocker {
private static final Set<String> AD_HOSTS = new HashSet<>();
public static boolean isAd(String url) {
try {
return isAdHost(getHost(url));
} catch (MalformedURLException e) {
Log.e("Devangi..", e.toString());
return false;
}
}
private static boolean isAdHost(String host) {
if (TextUtils.isEmpty(host)) {
return false;
}
int index = host.indexOf(".");
return index >= 0 && (AD_HOSTS.contains(host) ||
index + 1 < host.length() && isAdHost(host.substring(index + 1)));
}
public static WebResourceResponse createEmptyResource() {
return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes()));
}
public static String getHost(String url) throws MalformedURLException {
return new URL(url).getHost();
}
}
And use this WebViewClient in your oncreate like:
webview.setWebViewClient(new MyWebViewClient());
I am using GCM with my phonegap android app. The problem I am facing is, the app is able to receive the notification if its open in mobile, but not able to receive notification if its closed. I have gone through the java code written is working fine. But its not able to communicate with javascript code written for receiving message
public class GCMIntentService extends GCMBaseIntentService {
public static final String ME="GCMReceiver";
public GCMIntentService() {
super("GCMIntentService");
}
private static final String TAG = "GCMIntentService";
#Override
public void onRegistered(Context context, String regId) {
Log.v(ME + ":onRegistered", "Registration ID arrived!");
Log.v(ME + ":onRegistered", regId);
JSONObject json;
try
{
json = new JSONObject().put("event", "registered");
json.put("regid", regId);
Log.v(ME + ":onRegisterd", json.toString());
// In this case this is the registration ID
GCMPlugin.sendJavascript( json );
}
catch( JSONException e)
{
// No message to the user is sent, JSON failed
Log.e(ME + ":onRegisterd", "JSON exception");
}
}
#Override
public void onUnregistered(Context context, String regId) {
Log.d(TAG, "onUnregistered - regId: " + regId);
}
#Override
protected void onMessage(Context context, Intent intent) {
Log.d(TAG, "onMessage - context: " + context);
// Extract the payload from the message
Bundle extras = intent.getExtras();
if (extras != null) {
try
{
Log.v(ME + ":onMessage extras ", extras.getString("message"));
JSONObject json;
json = new JSONObject().put("event", "message");
json.put("message", extras.getString("message"));
json.put("msgcnt", extras.getString("msgcnt"));
Log.v(ME + ":onMessage ", json.toString());
GCMPlugin.sendJavascript( json );
// Send the MESSAGE to the Javascript application
}
catch( JSONException e)
{
Log.e(ME + ":onMessage", "JSON exception");
}
}
}
#Override
public void onError(Context context, String errorId) {
Log.e(TAG, "onError - errorId: " + errorId);
}
}
I think you are using GCMPlugin plugin.There is nowhere written anything how to handle push notifications when the app will go background or being destroyed.In my opinion its better to remove this plugin and use PushPlugin ,otherwise you have to make a drastic change not only to the GCMIntentservice.java but also to the GCMPlugin.java.
Recently I have encounter a problem with the web application. I'm using the spring mvc restful application together with hibernate as jpa.
The client could build a xml file using this format:
<SCCF>
<registerSCCF>...</registerSCCF>
...
<registerSCCF>...</registerSCCF>
</SCCF>
The web app will then mapping every data inside registerSCCF tag to a class and save it in the database.
Now I am suffering with the problem that when i test it using soapui and multithreading test, i always get the exception
[ERROR] an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in draft.persistence.entity.dcrm.CustomersNoneSSO entry (don't flush the Session after an exception occurs)
or
Caused by: org.hibernate.HibernateException: Flush during cascade is dangerous
or
org.hibernate.SessionException: Session is closed!
Here is the service layer code:
#Transactional("dcrm")
public boolean postSCCFService(SCCFVO sccf){
CustomersNoneSSO cns = new CustomersNoneSSO();
cns.setAppid(sccf.getAppid());
cns.setCustomer_name(sccf.getCustomer_name());
cns.setCustomer_gender(sccf.getCustomer_gender());
cns.setContact_mobile(sccf.getContact_mobile());
cns.setContact_email(sccf.getContact_email());
cns.setAddress_province(sccf.getAddress_province());
cns.setAddress_city(sccf.getAddress_city());
cns.setCustomer_address(sccf.getCustomer_address());
cns.setCustomer_occupation(sccf.getCustomer_occupation());
cns.setPurchase_brand(sccf.getPurchase_brand());
cns.setPurchase_model(sccf.getPurchase_model());
cns.setPurchase_date(sccf.getPurchase_date());
cns.setPurchase_budget(sccf.getPurchase_budget());
cns.setOwncar_selected(sccf.getOwncar_selected());
cns.setOwncar_model(sccf.getOwncar_model());
cns.setTestdrive_permission(sccf.getTestdrive_permission());
cns.setMarketing_permission(sccf.getMarketing_permission());
Timestamp t = new Timestamp(new Date().getTime());
cns.setInsert_timestamp(t);
cns.setUpdate_timestamp(t);
cnsDao.makePersistent(cns);
}
if i set all the setter to static values like:
cns.setContact_email("test#test.test");
instead of using the value from the parameter, then the app runs well with the multithreading test.
There is the controller calls the service method:
#RequestMapping(value = "/test",method=RequestMethod.POST)
public #ResponseBody SCCFResponseList getPostResults(#RequestBody SCCFVOList sccf){
...
for(SCCFVO sccfvo : sccf.getSCCFVOList()){
...
boolean result = sccfservice.postSCCFService(sccfvo);
...
}
...
}
public class SCCFVOList {
And here is the request body class:
#XmlElement(name="registerSCCF")
public class SCCFVOList {
private Vector<SCCFVO> SCCFVOList = null;
public Vector<SCCFVO> getSCCFVOList(){
return SCCFVOList;
}
public void setSCCFVOList(Vector<SCCFVO> SCCFVOList){
this.SCCFVOList = SCCFVOList;
}
}
And here the dao
public class CNSDao extends GenericHibernateDAO<CustomersNoneSSO, Long> {}
public abstract class GenericHibernateDAO<T, ID extends Serializable>
implements GenericDAO<T, ID> {
private Class<T> persistentClass;
private Session session;
SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public GenericHibernateDAO() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
#SuppressWarnings("unchecked")
public void setSession(Session s) {
this.session = s;
}
protected Session getSession() {
session = sessionFactory.getCurrentSession();
if (session == null)
throw new IllegalStateException(
"Session has not been set on DAO before usage");
return session;
}
public Class<T> getPersistentClass() {
return persistentClass;
}
#SuppressWarnings("unchecked")
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
public void makeTransient(T entity) {
getSession().delete(entity);
}
...
}
There should be something wrong either the controller method or the service method. Still no idea what was wrong.
Your dao is flawed.
Your dao is a singleton, there is only one. The Hibernate Session object isn't thread safe and shouldn't be used across threads.
You have 1 dao, 2 threads, Thread one gets instance X1 of a session, Thread two resets it to instance X2 now suddenly they share the same session, not to mention Thread 1 might even be operating on 2 different sessions.
As I mentioned in the comments NEVER store the Session in an instance variable. Remove it.
public abstract class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {
private Class<T> persistentClass;
private SessionFactory sessionFactory;
public GenericHibernateDAO() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
Also I would suggest dropping this and instead use Spring Data JPA saves you the trouble of creating and maintaining your own generic dao. (You mention you use JPA, if the entities are annotated it should be quite easy to do).
I work on an application that using PhoneGap. For the moment, I only test it on Android. I have several pages in my application that need the geolocation feature.
So I made a JS to handle it with this line of code (of course it's not my unique line of code) :
navigator.geolocation.watchPosition(successGeolocation, errorGeolocation, {maximumAge: 5000, enableHighAccuracy: true});
The geolocation need to be the most accurate possible so I use the GPS and it can take some time to have a GPS Fix.
The problem is when the user navigates from one page to another. The WatchPosition stop (it's normal because the user load an other page) and when I recall it the GPS need to Fix again.
It's very annoying and I search a solution to keep the GPS active. Someone has an idea for me ? Maybe with a plugin or a native Android LoC I can keep it active during all the application life ?
Thanks.
First step you should create an Android plugin that will give you API for receiving the location data.
Creating a plugin is quite easy and is explained here:
Plugin Development Guide and Developing a Plugin on Android.
You can also see an example of creating a Cordova plugin here.
Next, create a location monitor class. You can make it singleton and initialize it from your main activity.
Here is a simple, but working code I compiled from several sources and many tests to fit my needs.
The main code is taken from here, though I simplified it as much as was possible.
public class LocationMonitor
{
private LocationListener locationListener = null;
private LocationManager locationManager = null;
private Location location = null;
public LocationMonitor()
{
}
public void startGPSActivity(Context context)
{
LocationLooper looper = new LocationLooper();
looper.start();
while (!looper.isReady())
{
}
looper.handler.post(new LocationBootstrapper(context));
}
public void stopGPSActivity()
{
locationManager.removeUpdates(locationListener);
}
public Location getLocation()
{
return location;
}
private class LocationLooper extends Thread
{
private Handler handler;
private LocationLooper()
{
}
public void run()
{
Looper.prepare();
this.handler = new Handler();
Looper.loop();
}
public boolean isReady()
{
return this.handler != null;
}
}
private class LocationBootstrapper implements Runnable
{
private Context context;
private LocationBootstrapper(Context context)
{
this.context = context;
}
public void run()
{
locationListener = new LocationListenerImpl();
locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 1, locationListener);
}
}
private class LocationListenerImpl implements LocationListener
{
private LocationListenerImpl()
{
}
#Override
public void onLocationChanged(Location location)
{
LocationMonitor.this.location = location;
Log.i("LocationMonitor", "New location: lat= " + location.getLatitude() + " lng=" + location.getLongitude() + " acc=" + location.getAccuracy());
}
#Override
public void onProviderDisabled(String provider)
{
}
#Override
public void onProviderEnabled(String provider)
{
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
}
}
}
Now access the LocationMonitor class from your plugin and you have your desired solution - page changes will not re-initialize your GPS and location data is available to your PhoneGap app.
Cheers