Reputation: 450
I have been able to call JavaScript from C# inside the MainActivity
but I'm trying to do so from an object. The majority of my app runs inside a WebView, my JavaScript calls to my C# Interface invoking an asynchronous function and when it's complete I would like to call back to my JavaScript but am unable to do so. Here is my current setup:
In my MainActivity
I setup my WebView as such:
browser = FindViewById<WebView>(Resource.Id.mainView);
browser.SetInitialScale(1);
browser.SetWebChromeClient(new GeoWebChromeClient());
browser.Settings.UseWideViewPort = true;
browser.Settings.LoadWithOverviewMode = true;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
WebView.SetWebContentsDebuggingEnabled(true);
}
browser.Settings.SetGeolocationEnabled(true);
browser.Settings.JavaScriptEnabled = true;
browser.AddJavascriptInterface(new JSCSMedium(this, ref browser), "Android");
browser.LoadUrl("file:///android_asset/web/index.html");
Then inside the JSCSMedium object I have an asynch function:
[Export]
[JavascriptInterface]
public void SyncApps()
{
Task t = Task.Run(() => {
IList<ApplicationInfo> tempApps = Application.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MatchDirectBootAware);
string packageName = "";
string appName = "";
for (int i = 0; i < tempApps.Count(); i++)
{
packageName = tempApps[i].PackageName;
appName = tempApps[i].LoadLabel(manager);
var root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var filePath = System.IO.Path.Combine(root, "system");
filePath = System.IO.Path.Combine(filePath, packageName);
if (!System.IO.Directory.Exists(filePath))
{
System.IO.Directory.CreateDirectory(filePath);
}
filePath = System.IO.Path.Combine(filePath, "icon.png");
if (!System.IO.File.Exists(filePath))
{
Drawable icon = tempApps[i].LoadIcon(Application.Context.PackageManager);
BitmapDrawable bd = (BitmapDrawable)icon;
CreateAppIcon(bd.Bitmap, packageName);
}
Intent intent = Application.Context.PackageManager.GetLaunchIntentForPackage(packageName);
if (intent != null)
{
apps.Add(tempApps[i]);
}
}
});
}
If I don't do the C# as an async function it runs and returns data fine, but this process takes a bit of time and blocks the app temporarily. Inside my MainActivity
I can call JavaScript just fine with:
browser.EvaluateJavascript("javascript: alert('fooBar');", null);
But browser
is not accessible inside the JSCSMedium
. I've tried passing the browser
object as a reference and normally but it throws an exception stating that the EvaluateJavascript
function must be called on the same thread as where it was instantiated. I've also tried sending a reference of my MainActivity
to the JSCSMedium
and call a function inside the MainActivity
to run the EvaluateJavascript
but it seems to do nothing. No error, not crash, just nothing.
Upvotes: 2
Views: 3810
Reputation: 1084
The problem is Task.Run
forces the code to run in the thread pool, and browser.EvaluateJavascript
needs to run on the main thread.
You have at least two options here, depending on your needs:
1) Run the EvaluateJavascript call inside the Task.Run
block with something like:
var h = new Handler(Looper.MainLooper);
h.Post(() => browser.EvaluateJavascript("javascript: alert('fooBar');", null));
2) Run the EvaluateJavascript call outside the Task.Run
block:
[Export]
[JavascriptInterface]
public async void SyncApps()
{
await Task.Run(() => {
//...
});
browser.EvaluateJavascript("javascript: alert('fooBar');", null);
}
Not really sure if you can change the return type of SyncApps()
. If JS doesn't complain, you better change that too.
Upvotes: 1