I'm using JavaScript interface for checking if Google's StreetView is available. My problem is that from android 3.0 code stopped working, and I am unnable to find why. Problem is that methods from "JavascriptCheck" interface are never called and Logcat doesn't show any errors.
Java code:
public void showStreetView(GeoPoint geoPoint) {
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JavascriptCheck(), "Android");
lat = geoPoint.getLatitudeE6()/1E6;
lon = geoPoint.getLongitudeE6()/1E6;
webView.loadDataWithBaseURL("", context.getString(R.string.html_streetview, lat, lon), "text/html", "UTF-8", "");
}
public class JavascriptCheck {
public void hasStreetview(boolean hasStreetview) {
if (hasStreetview) {
openStreetView();
} else {
Toast.makeText(context, context.getString(R.string.loc_no_street_view), Toast.LENGTH_SHORT).show();
}
}
}
WebView in layout file:
<WebView android:id="#+id/webView"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:visibility="gone">
</WebView>
JavaScript string:
<string name="html_streetview">
<html>
<head>
<script src=\"http://maps.google.com/maps?file=api&v=2& sensor=false\" type=\"text/javascript\"/>
</head>
<body>
<script type=\"text/javascript\">
var testPoint = new GLatLng(%1$s, %2$s);
var svClient = new GStreetviewClient();
svClient.getNearestPanoramaLatLng(testPoint, function (nearest) {
if ((nearest !== null) && (testPoint.distanceFrom(nearest) <= 100)) {
Android.hasStreetview(true);
} else {
Android.hasStreetview(false);
}
});
</script>
</body>
</html>
</string>
Solved my problem long ago, just wanted to share with others. Honeycomb and later Android versions require that you use full html <script> tags. Also it is better to keep script string in assets folder. My assets/index.html looks like this now:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<script src="http://maps.googleapis.com/maps/api/js?sensor=false" type="text/javascript"></script>
<script type="text/javascript">
var sv = new google.maps.StreetViewService();
function hasStreet(lat, lon) {
var point = new google.maps.LatLng(lat, lon);
sv.getPanoramaByLocation(point, 50, isSVAvailable);
}
function isSVAvailable(data, status) {
if (status == google.maps.StreetViewStatus.OK) {
Android.hasStreetview(true);
} else {
Android.hasStreetview(false);
}
}
</script>
</head>
<body></body>
</html>
I too was using this function and have seen it broken since I tried upgrading my app for ICS. It seems that the Javascript won't execute if you have an external src link. If you take out the javascript src link and add some logging you'll see that the script will run (and obviously return false all the time).
I know in the docs they recommend not using javascript that calls into your native code unless you control all elements in the javascript but perhaps now they explicitly stop code from running that references an external resource?
Related
I'm a noob to Flutter Web. I have a package that I'm trying to create support for in Flutter Web, but it uses a webview for some functions. Webviews aren't supported in Flutter Web so I'm using a IFrameElement and ui.platformViewRegistry.registerViewFactory() to act like a webview. I'm passing an HTML String to be loaded rather than a file.
I need to be able to run JS functions and get data from JS. I've tried a lot of different things with events and event listeners, also context.callMethod() and none of it has worked so far. Is there a simple way to accomplish this?
For reference, I am using the Summernote library and I can run something like \$('#summernote').summernote('reset'); to reset the Summernote editor. Sometimes I need to get data from JS so I am running var str = \$('#summernote').summernote('code'); console.log(str); which gives me the HTML code in the editor.
Thanks in advance!
Code for reference:
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:html_editor_enhanced/html_editor.dart';
import 'package:html_editor_enhanced/utils/pick_image.dart';
import 'package:path/path.dart' as p;
import 'package:flutter/material.dart';
import 'dart:html' as html;
import 'dart:js' as js;
import 'dart:ui' as ui;
bool callbacksInitialized = false;
js.JsObject jsDocument;
class HtmlEditorWidgetWeb extends StatelessWidget {
HtmlEditorWidgetWeb({
Key key,
this.value,
this.height,
this.useBottomSheet,
this.imageWidth,
this.showBottomToolbar,
this.hint,
this.callbacks,
this.toolbar,
this.darkMode
}) : super(key: key);
final String value;
final double height;
final bool useBottomSheet;
final double imageWidth;
final bool showBottomToolbar;
final String hint;
final UniqueKey webViewKey = UniqueKey();
final Callbacks callbacks;
final List<Toolbar> toolbar;
final bool darkMode;
final String createdViewId = 'html_editor_web';
#override
Widget build(BuildContext context) {
String summernoteToolbar = "[\n";
for (Toolbar t in toolbar) {
summernoteToolbar = summernoteToolbar +
"['${t.getGroupName()}', ${t.getButtons()}],\n";
}
summernoteToolbar = summernoteToolbar + "],";
String darkCSS = "";
if ((Theme.of(context).brightness == Brightness.dark || darkMode == true) && darkMode != false) {
darkCSS = "<link href=\"packages/html_editor_enhanced/assets/summernote-lite-dark.css\" rel=\"stylesheet\">";
}
String htmlString = """
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="description" content="Flutter Summernote HTML Editor">
<meta name="author" content="xrb21">
<title>Summernote Text Editor HTML</title>
<script src="main.dart.js" type="application/javascript"></script>
<script src="app.js" defer></script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote#0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote#0.8.18/dist/summernote-lite.min.js"></script>
$darkCSS
<script>
function test() {
console.log("Listening");
}
</script>
</head>
<body>
<div id="summernote-2"></div>
<script type="text/javascript">
\$('#summernote-2').summernote({
placeholder: "$hint",
tabsize: 2,
height: ${height - 125},
maxHeight: ${height - 125},
toolbar: $summernoteToolbar
disableGrammar: false,
spellCheck: false
});
document.addEventListener("setFS", function(){
console.log('fired');
\$('#summernote-2').summernote("fullscreen.toggle");
});
</script>
<style>
body {
display: block;
margin: 0px;
}
.note-editor.note-airframe, .note-editor.note-frame {
border: 0px solid #a9a9a9;
}
.note-frame {
border-radius: 0px;
}
</style>
</body>
</html>
""";
html.window.onMessage.forEach((element) {
print('Event Received in callback: ${element.data}');
});
// todo use postmessage and concatenation to accomplish callbacks
final html.IFrameElement iframe = html.IFrameElement()
..width = MediaQuery.of(context).size.width.toString() //'800'
..height = MediaQuery.of(context).size.height.toString() //'400'
..srcdoc = htmlString
..style.border = 'none'
..onLoad.listen((event) async {
html.document.on['setFS'].listen((html.Event event) {
print("HEY! I'M LISTENING!");
});
html.document.dispatchEvent(html.Event("setFS"));
});
ui.platformViewRegistry.registerViewFactory(
createdViewId, (int viewId) => iframe);
return Column(
children: <Widget>[
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
child: HtmlElementView(
viewType: createdViewId,
)
)
),
],
);
}
}
A little bit hacky, but here's the solution I use:
Dart -> JS
In dart:
html.window.postMessage(//data to send here, "*");
and in the IframeElement HTML <script>:
window.parent.addEventListener('message', handleMessage, false);
function handleMessage(e) {
var data = e.data;
}
I personally use JSON when sending data to make it easier to send/receive/parse. So in Dart that is a Map<String, dynamic>, sent like this:
final data = Map<String, dynamic>{
//your data here
}
final jsonEncoder = JsonEncoder();
final json = jsonEncoder.convert(data);
html.window.postMessage(json, "*");
and in JS:
window.parent.addEventListener('message', handleMessage, false);
function handleMessage(e) {
var data = JSON.parse(e.data);
}
A suggestion would be to create a unique key/string that you can pass in between JS and Dart so you make sure you are intercepting the correct postMessage every time.
JS -> Dart
In the IframeElement HTML <script>:
window.parent.postMessage(//data to send, "*");
and in Dart:
html.window.onMessage.listen((event) {
var data = event.data;
});
Again, I use JSON to communicate because I think it makes things easier. Use JSON.stringify() in JS and json.decode() in Dart.
I have an issue with a web view I'm using.
The javascript files used to render the view properly MUST be loaded in a specific order.
On browser (chrome mobile; safari desktop, ...) it works well.
However in my web view the result is unpredictable.
This is one of the latests failing run I had:
The HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="js/script1.js"></script>
<script src="js/script2.js"></script>
<script src="js/script3.js"></script>
<script src="js/script4.js"></script>
... More scripts...
</body>
</html>
Here is the Java code:
mWebView = (WebView)findViewById(R.id.webView);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
//mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setHorizontalFadingEdgeEnabled(false);
mWebView.setScrollbarFadingEnabled(false);
mWebView.setVerticalFadingEdgeEnabled(false);
mWebView.setOverScrollMode(View.OVER_SCROLL_NEVER);
mWebView.loadUrl("http://192.168.1.24:8000/path/to/index.html");
The issue is that randomly, javascript files are interpreted in wrong order or just not loaded.
From my understanding, javascript MUST be loaded in declaration order.
Am I wrong ?
Is there anything I can do to change that behavior ? (other than concatenating all JS together..)
Your scripts will work on the web site but for make it work on the android you need to load the scripts manually. See the function how it load the script from assets.
private void injectScriptFile(WebView view, String scriptFile) {
InputStream input;
try {
input = getAssets().open(scriptFile);
byte[] buffer = new byte[input.available()];
input.read(buffer);
input.close();
// String-ify the script byte-array using BASE64 encoding !!!
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
view.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
// Tell the browser to BASE64-decode the string into your script !!!
"script.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(script)" +
"})()");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
See Here Example
also make sure you enabled JS:
webSettings.setJavaScriptEnabled(true);
and the manifest ofcourse:
<uses-permission android:name="android.permission.INTERNET" />
First, I want to thank you for the efforts in answering.
Second, I created a site that contains a plug-in of Google Earth, and I want to use a local KML file to show two coordinates.
HTML code:
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"> </script>
<script type="text/javascript">
var ge;
google.load("earth", "1", {"other_params":"sensor=false"});
var fso, kmlString, fh;
function init() {
google.earth.createInstance('map3d', initCB, failureCB);
}
function initCB(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
}
function failureCB(errorCode) {
}
google.setOnLoadCallback(init);
</script>
</head>
<body>
<div id="map3d" style="height: 300px; width: 400px;"></div>
</body>
</html>
KML code:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Simple placemark</name>
<description>Attached to the ground. Intelligently places itself
at the height of the underlying terrain.</description>
<Point>
<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>
</Point>
</Placemark>
</kml>
I searched Google for some answers, but nothing helped me to understand how to combine between the HTML and KML (Google Earth Plug-in). please help me.
Thank you very much,
Orian.
function initCallback(pluginInstance) {
ge = pluginInstance;
ge.getWindow().setVisibility(true);
// Earth is ready, we can add features to it
addKmlFromUrl('http://path/to/your.kml');
}
function addKmlFromUrl(kmlUrl) {
var link = ge.createLink('');
link.setHref(kmlUrl);
var networkLink = ge.createNetworkLink('');
networkLink.setLink(link);
networkLink.setFlyToView(true);
ge.getFeatures().appendChild(networkLink);
}
or
function addKmlFromUrl(kmlUrl) {
google.earth.fetchKml(ge, kmlUrl, kmlFinishedLoading);
}
function kmlFinishedLoading(kmlObject) {
if (kmlObject) {
ge.getFeatures().appendChild(kmlObject);
}
}
https://developers.google.com/earth/articles/earthapikml
"use a local KML file"
Using the file:// handler in the browser may satisfy your needs, using a web-browser to access local files has it's own gotchas though, from security restrictions onwards.
I have a very internationalised website, however I need to produce a pop-up specifically for our UK customers.
What I require is:
On page load: Is the user from the UK?
If yes then show div.
Else
Div remains hidden.
You can do this using freegeoip.
Since you mentioned that you want to use plain JavaScript (not jQuery), you should use JSONP to get the country:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8>
<title>UK localisation</title>
</head>
<body>
<div id="myDiv" style="display:none">
<h1>Kittens</h1>
</div>
<script>
function toggleDiv(content) {
console.log(content.country_code);
if(content.country_code === 'GB') //Or GBR, or UK, I'm not sure.
{
document.getElementById('myDiv').style.display = "inline";
}
else
{
alert("You are not from UK, you are from " + content.country_code);
document.getElementById('myDiv').style.display = "none";
}
}
window.onload = function()
{
// create script element
var script = document.createElement('script');
// passing src with callback name
script.src = 'http://freegeoip.net/json/?callback=toggleDiv';
// insert script to document and load content
document.body.appendChild(script);
}
</script>
</body>
</html>
i've tried to write a simple youtube request to search video with youtube javascript api v3.
This is the source code:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function showResponse(response) {
var responseString = JSON.stringify(response, '', 2);
document.getElementById('response').innerHTML += responseString;
}
// Called automatically when JavaScript client library is loaded.
function onClientLoad() {
gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
}
// Called automatically when YouTube API interface is loaded
function onYouTubeApiLoad() {
// This API key is intended for use only in this lesson.
gapi.client.setApiKey('API_KEY');
search();
}
function search() {
var request = gapi.client.youtube.search.list({
part: 'snippet',
q:'U2'
});
// Send the request to the API server,
// and invoke onSearchRepsonse() with the response.
request.execute(onSearchResponse);
}
// Called automatically with the response of the YouTube API request.
function onSearchResponse(response) {
showResponse(response);
}
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="https://apis.google.com/js/client.js?onload=onClientLoad" type="text/javascript"></script>
</head>
<body>
<pre id="response"></pre>
</body>
</html>
When i load this page on google chrome (updated), nothing happens, the page remains blank.
I have request the API Key for browser apps (with referers) and copied in the method gapi.client.setApiKey.
Anyone can help me?
Thanks
Try this example here
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google AJAX Search API Sample</title>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// How to search through a YouTube channel aka http://www.youtube.com/members
google.load('search', '1');
function OnLoad() {
// create a search control
var searchControl = new google.search.SearchControl();
// So the results are expanded by default
options = new google.search.SearcherOptions();
options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
// Create a video searcher and add it to the control
searchControl.addSearcher(new google.search.VideoSearch(), options);
// Draw the control onto the page
searchControl.draw(document.getElementById("content"));
// Search
searchControl.execute("U2");
}
google.setOnLoadCallback(OnLoad);
</script>
</head>
<body style="font-family: Arial;border: 0 none;">
<div id="content">Loading...</div>
</body>
</html>
When you use <script src="https://apis.google.com/js/client.js?onload=onClientLoad" ..></script>
you have to upload the html file somewhere online or use XAMPP on your PC
To use html for searching YT videos, using Javascript on PC, as I know, we need to use other codings:
1- Use javascript code similar to this for API version 2.0. Except only the existence of API KEY v3.
2- Use the jQuery method "$.get(..)" for the purpose.
See:
http://play-videos.url.ph/v3/search-50-videos.html
For more details see (my post "JAVASCRIPT FOR SEARCHING VIDEOS"):
http://phanhung20.blogspot.com/2015_09_01_archive.html
var maxRes = 50;
function searchQ(){
query = document.getElementById('queryText').value;
email = 'https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=50'+
'&order=viewCount&q='+ query + '&key=****YOUR API3 KEY*****'+
'&callback=myPlan';
var oldsearchS = document.getElementById('searchS');
if(oldsearchS){
oldsearchS.parentNode.removeChild(oldsearchS);
}
var s = document.createElement('script');
s.setAttribute('src', email);
s.setAttribute('id','searchS');
s.setAttribute('type','text/javascript');
document.getElementsByTagName('head')[0].appendChild(s);
}
function myPlan(response){
for (var i=0; i<maxRes;i++){
var videoID=response.items[i].id.videoId;
if(typeof videoID != 'undefined'){
var title=response.items[i].snippet.title;
var links = '<br><img src="http://img.youtube.com/vi/'+ videoID +
'/default.jpg" width="80" height="60">'+
'<br>'+(i+1)+ '. <a href="#" onclick="playVid(\''+ videoID +
'\');return false;">'+ title + '</a><br>';
document.getElementById('list1a').innerHTML += links ;
}
}
}
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
<input type="text" value="abba" id="queryText" size="80">
<button type="button" onclick="searchQ()">Search 50 videos</button>
<br><br>
<div id='list1a' style="width:750px;height:300px;overflow:auto;
text-align:left;background-color:#eee;line-height:150%;padding:10px">
</div>
I used the original code that Tom posted, It gave me 403 access permission error. When I went back to my api console & checked my api access time, it was expired. So I recreated the access time for the api. It regenerated new time. And the code worked fine with results.
Simply i must make request from a web server.
Thanks all for your reply