Enable Geolocation in a WebView (Android)

Enable Geolocation in a WebView (Android)


转载于:

https://turbomanage.wordpress.com/2012/04/23/how-to-enable-geolocation-in-a-webview-android/

HTML5 Geolocation lets you access the browser’s location information from Javascript. For a quick demo, visit google.com in a mobile browser and observe your location at the bottom of the page. Geolocation works “out of the box” in the Android browser or Chrome Beta for Android. The first time you visit a Web site that wants to know your location, the browser will prompt you to allow it.

However, if you’re writing an Android app that uses a WebView to display Web content, you must specifically enable geolocation in the WebView in order for pages that use it to work properly. To illustrate, I’ve put together a minimal Android app that uses a WebView with geolocation enabled. The easiest way to use the sample is to create a new Android Project called GeoWebView (package name com.example.webview) and then copy and replace the generated sample code with the files below.

First, the Android manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.webview"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="5" />
 
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".GeoWebViewActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Only two permissions are required: INTERNET and ACCESS_FINE_LOCATION. This example uses the NoTitleBar theme to display the WebView content full screen.

Next, our layout (main.xml):

<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webView1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

Not much to it, just a WebView.

And finally, here is the complete app, consisting only of GeoWebViewActivity:

package com.example.webview;
 
import android.app.Activity;
import android.os.Bundle;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
/**
 * A minimal WebView app with HTML5 geolocation capability
 *
 * @author David M. Chandler
 */
public class GeoWebViewActivity extends Activity {
 
    /**
     * WebViewClient subclass loads all hyperlinks in the existing WebView
     */
    public class GeoWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            // When user clicks a hyperlink, load in the existing WebView
            view.loadUrl(url);
            return true;
        }
    }
 
    /**
     * WebChromeClient subclass handles UI-related calls
     * Note: think chrome as in decoration, not the Chrome browser
     */
    public class GeoWebChromeClient extends WebChromeClient {
        @Override
        public void onGeolocationPermissionsShowPrompt(String origin,
                GeolocationPermissions.Callback callback) {
            // Always grant permission since the app itself requires location
            // permission and the user has therefore already granted it
            callback.invoke(origin, true, false);
        }
    }
 
    WebView mWebView;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mWebView = (WebView) findViewById(R.id.webView1);
        // Brower niceties -- pinch / zoom, follow links in place
        mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        mWebView.getSettings().setBuiltInZoomControls(true);
        mWebView.setWebViewClient(new GeoWebViewClient());
        // Below required for geolocation
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.getSettings().setGeolocationEnabled(true);
        mWebView.setWebChromeClient(new GeoWebChromeClient());
        // Load google.com
        mWebView.loadUrl("http://www.google.com");
    }
 
    @Override
    public void onBackPressed() {
        // Pop the browser back stack or exit the activity
        if (mWebView.canGoBack()) {
            mWebView.goBack();
        }
        else {
            super.onBackPressed();
        }
    }
}

The onCreate() method obtains the WebView and enables Javascript and pinch/zoom. The last few lines before loading the URL google.com are the most important. In addition to calling setGeolocationEnabled(true), we configure the WebView by passing it two kinds of “clients”: a chrome client, which handles UI, or “chrome”, events (no relation to the browser bearing that name) for your WebView, and a basic client, which handles content-related events such as errors and form resubmission.

First, let’s look at the WebViewClient as implemented in the inner class GeoWebViewClient. It’s not strictly necessary for geolocation, but does something that almost all WebView apps do, which is to load any new URLs (such as when the user touches a hyperlink) in the same WebView. The default behavior is to fire an Intent to load in another browser, which is rarely what you want.

Next, a WebChromeClient as implemented in GeoWebChromeClient is essential for geolocation to work in the WebView. Whenever the WebView encounters Javascript using the Geolocation API, the WebView calls the method onGeolocationPermissionsShowPrompt() in its associated WebChromeClient. The name of the method is slightly misleading in this context, as neither the WebView nor the WebChromeClient actually shows a prompt. If you want to show a prompt, this is the place to do it, and you can find an example in the AOSP source code for packages/apps/Browser/src/com/android/browser/Tab.java. Fortunately, since the app itself declares the ACCESS_FINE_LOCATION permission, the user has effectively already granted permission, and you can simply invoke the callback with the second argument (allow) == true.

The final touch on our app is to wire up the phone’s back button in the onBackPressed() method. If we can go back in the WebView, we do that; otherwise, we back out of the app altogether.

注意:记得要把手机访问位置的权限打开,否则定不了位置!

HTML5 for the Mobile Web - a guide to the Geolocation API

转载自:http://mobiforge.com/design-development/html5-mobile-web-a-guide-geolocation-api

In this second article in our series on HTML5 for mobile web (first part here), we cover the Geolocation API. For mobile users, location-based services are hugely compelling. Long the holy grail of mobile applications, and something of a missed opportunity for service providers, the addition of location-awareness to mobile apps has made for some very exciting use cases. 

Apps and services can take on a whole new dimension when location is added into the mix. Search results gain an extra relevance cue when a nearby option is added, as location information provides a very useful and intuitive filter. Navigation- and map-based apps and games become possible. Even augmented reality apps are no longer beyond the reach of the mobile browser, as illustrated below.


Figure 1: Web-based augmented reality proof of concept on mobile browser, by Dominique Hazael-Massieux

In the following sections we take a look at HTML5 Geolocation from a mobile perspective. We look at the technologies involved and work through some map-based examples to see what the Geolocation API has to offer.

How Geolocation works

There are a variety of techniques and sensor data that modern mobile devices can tap to figure out the location of a device with varying degrees of accuracy. The most common are discussed briefly below.

  • GPS: Perhaps the most familiar method of geolocation is via the GPS receiver. Many modern smartphones come with GPS sensor as standard. GPS geolocation works by detecting the signal from a number of satellites in the sky, typically four or more; by using a mathematical technique called trilateration the device can determine its location based on the timing of the statellite signals.
    A downside of GPS is that it can often take a some time to lock onto enough satellites to provide a location fix. It also requires a clear view of the sky, so if you're indoors, you're out of luck.
  • A-GPS: (Assisted GPS) This approach helps to address some of the shortcomings of GPS, by augmenting the GPS receiver's capabilities by supplying some data from the service-provider network which helps to locate the device.
  • Wi-Fi based positioning: This approach was developed to address the shortcomings of Cell Triangulation and GPS approaches. The basic concept is that by correlating Wi-Fi signals with GPS and other location data, a clue to the device's location is provided when that same Wi-Fi signal is encountered in the future.
  • Cell Triangulation: An approximate location can be calculated by using information about the location of the cell towers that a mobile device is connected to at any time. This concept has been discussed previously here on mobiForge when we took a look at the OpenCellID project.

Geolocation and privacy

Geolocation was at the outset somewhat haunted by privacy concerns. Such concerns have, to a certain extent, become more muted. Or at least the privacy controls that are in place are enough to satisfy the average user such that he or she is willing to give up some degree of privacy to benefit from some location-based service. The Geolocation API quite clearly addresses privacy and puts the user firmly in control by mandating that the user's permission must be obtained before location information can be shared. From the specification:

User agents must not send location information to Web sites without the express permission of the user. User agents must acquire permission through a user interface, unless they have prearranged trust relationships with users, as described below. The user interface must include the host component of the document's URI. Those permissions that are acquired through the user interface and that are preserved beyond the current browsing session (i.e. beyond the time when the browsing context is navigated to another URL) must be revocable and user agents must respect revoked permissions.

So, permission must be obtained from the user, the website hostname must be displayed, and it must be possible for the user to revoke permission.

This part of the specification is manifested in a similar way across the various mobile browsers. When a location request is made from a web page, a dialog is popped into the user’s view to request permission to access the location data (Android and iOS implementations are illustrated below).


Figure 2: Android (left) and iPhone (right) browsers requesting permission to access location information

Geolocation API details

In the Geolocation API, location is defined in terms of Position and Coordinate objects. A Coordinate has the properties listed in the table below.

PropertyDescription
latitudegeographic coordinate specified in decimal degrees
longitudegeographic coordinate specified in decimal degrees
altitude(optional) the height of the position in metres
accuracyrepresents the accuracy of the lat and lon values
altitudeAccuracy(optional) the accuracy of the altitude value, specified in metres
heading(optional) the current direction of travel of the device, in degrees between 0 and 360, clockwise from North
speed(optional) the current speed of the device, in metres per second

The Position object is simply a Coordinate coupled with a timestamp.

As shown in the table above, the properties altitudealtitudeAccuracyheading and speed are all optional. So only the latitudelongitude, and accuracy properties are guaranteed to be present. Therefore we will focus only on these properties in our example below.

Geolocation API Example

As an example to give a flavour of the Geolocation API, we will display a map in the browser, centered on the device’s current location. To start us off, we’ll use the Google Static Maps API, which returns simple static map images. Once we’re comfortable with this, we'll implement the more familiar interactive maps that we’re accustomed to seeing on our desktop computers.

First, the HTML. We add a div and a placeholder image. We will later update the src attribute of the image with the URL for the static map image.

<div id="map">
  <img id="static-map" src="placeholder.png" class="mfnotransform"/>
</div>

Next we will access the device position data by making a request to the Geolocation API. At its most basic, this involves a call like the following:

navigator.geolocation.getCurrentPosition(successCallback, errorCallback);

However, we need to check for support first. To do this we make the following call:

if (navigator && navigator.geolocation) {//do something}

We also need to define the successCallback function for when the position data is returned. It’s in this function that we will build the URL for the static map, centered on the latlon data we have just obtained.

function successCallback(position) {

The position object is passed as argument to this function. So now we need to know how to access the location data from this object. Recall from the table above the attributes of the coordinate object, and then accessing this required data is easy: position.coords.latitude and position.coords.longitude will give us the data we need. 

Once we have this information we need to pass it with our URL for the static map tile:

var mapUrl = "http://maps.google.com/maps/api/staticmap?center=";
mapUrl = mapUrl + position.coords.latitude + ',' + position.coords.longitude;
mapUrl = mapUrl + '&zoom=15&size=512x512&maptype=roadmap&sensor=true';

And finally, when this URL is built we simply set this as the src attribute of the img element we created above:

var imgElement = document.getElementById("static-map");
imgElement.src = mapUrl;
The whole thing looks like this:
<!DOCTYPE html>
<html>
<head>
<title>mobiForge geolocation demo</title>
<style>
#map {width:100%;height:100%;}
</style>
<script>
 
    function initGeolocation() {
 if (navigator && navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
        } else {
            console.log('Geolocation is not supported');
        }
}
 
function errorCallback() {}
 
function successCallback(position) {
      var mapUrl = "http://maps.google.com/maps/api/staticmap?center=";
      mapUrl = mapUrl + position.coords.latitude + ',' + position.coords.longitude;
      mapUrl = mapUrl + '&zoom=15&size=512x512&maptype=roadmap&sensor=false';
      var imgElement = document.getElementById("static-map");
      imgElement.src = mapUrl;
    }
</script>
</head>
<body onload="javascript:initGeolocation()">
<div id="map">
  <img id="static-map" src="placeholder.png" />
</div>
</body>
</html>

If you’re viewing this on a suitable device, click the link below the grey box to view the map, centered on your current location. (Full example code can be downloaded at end of article).

Click to try static map geolocation example

Google Maps API map

And for our next trick, we’ll implement a Google Map using the Maps API. So while the static map might be more suitable for low-end devices with CPU and RAM limitations, the interactive map can be used with more capable devices.

Adding the interactive map is only a little more complicated. We start off with similar HTML, but we will remove the image placeholder from the previous example, and just keep the div. We'll be loading the map into this div:

<div id="map">
</div>

Now, let's add in the Google Maps library by adding the following:

  <script src="http://maps.googleapis.com/maps/api/js?sensor=true"></script>

Note the sensor=true parameter. If information from a location sensor is being used, then this parameter must have a value of true. This parameter is used simply to indicate to Google that the map is being used with a geolocation sensor, and is required by Google as part of licensing terms with its data providers.

Now that the library is in place, we configure our map options and fire off a request for a map. The main options we will be interested in are the zoom level, the map type, and of course the latitude and longitude. We access the lat and lon from the position object as we did in the last example and pass to the LatLng function of the Google library.

  var map_options = {
    center: new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }

Next we grab our map div, and load the map into it:

  map_container = document.getElementById('map');
  var map = new google.maps.Map(map_container, map_options);

So, our updated success function now looks like this:

function successCallback(position) {
      var myLatlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      var map_options = {
        zoom: 15,
        center: myLatlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }
     map_container = document.getElementById('map');
     var map = new google.maps.Map(map_container, map_options);
    }

To see it in action, click the link below and the map should be loaded into the grey area. (Full example code can be downloaded at end of article).

Click to try Google Maps API Geolocation example

Geolocation Options

The Geolocation API provides an interface to further tweak device location requests. The available properties are outlined below. Of particular interest is the enableHighAccuracy option. As mentioned above, there are a number of different ways that the location of the device can be ascertained. Some approaches, such as cell tower triangulation, can be quick and dirty and provide an approximate location, while other approaches can be slower and more power-consuming, but more accurate, such as GPS. The Geolocation API offers an interface by which the desired location accuracy can be specified. Depending on your application, you may or may not require high accuracy.

PropertyDescriptionDefault
enableHighAccuracyindicates that the web application would like highest accuracy possiblefalse
timeoutthe length of time to wait before receiving user's locationinfinity
maximumAgeindicates that cached location information should be no older than this number of milliseconds0

Options are passed as an object with the position request. So, to request a location update that was no older than ten minutes, with high accuracy, and with a wait of no longer than one minute before timing out, the code would look something like the following:

navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {enableHighAccuracy:true, timeout:60000, maximumAge:600000})

Continuous position updates: watchPosition

In addition the API specifies a separate function to use if your application requires continuous position updates: watchPosition(). The arguments are the same as the getCurrentPosition(), but instead of requiring the developer to make repeated calls to getCurrentPosition() the developer's successCallback() function will be called whenever the user's location changes.

  var watchId = navigator.geolocation.watchPosition(successCallback);

Note that the watchPosition() function returns an integer. We can use this value to end monitoring of the user's location, so it's useful to store it for use later on.

When the application has no more use for the user's location, it should call clearWatch() to prevent the successCallback() function from being triggered any more. This function takes as parameter an integer value corresponding to the ID of the watch to be cleared - the value returned by the watchPosition() call.

  navigator.geolocation.clearWatch(watchId);

Geolocation API example: Continuous position updates

For our final example, we'll build on the previous examples, but this time we'll use watchPosition() to continuously re-center the map on the device location whenever the location is updated. To get the full benefit of this example you'll need to get up and run around outside for a few minutes while looking at this page on your phone.

First, we modify our code to use the watchPosition() function instead of getCurrentPosition(), and we'll store the return value for later use. As with getCurrentPosition, we specify the success callback function. This time though, we'll add the enableHighAccuracy option so that we'll notice smaller movements.

  var watchId = navigator.geolocation.watchPosition(successCallback, 
                                                    errorCallback,
                                                    {enableHighAccuracy:true,timeout:60000,maximumAge:0});

Now we define the callback function. We only need a small modification from our previous example. This time, if it's the first access, we need to create the map and center it on the device location. Whenever watchPosition() reports any subsquent change to the position, we will use the panTo method from the Google Maps API to re-center the map. We have also zoomed the map a little bit more, so that its movement will be more noticeable.

The updated function looks like this:

function successCallback(position) {
      var myLatlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      if(map == undefined) {
        var myOptions = {
          zoom: 15,
          center: myLatlng,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        }
        map = new google.maps.Map(document.getElementById("map"), myOptions);
      }
      else map.panTo(myLatlng);
    }

That's it, now out you go for a jog and click the link below the grey box to see this thing in action. (Full example code can be downloaded at end of article).

Click to try Geolocation API Continuous Position example

A shortcoming in this example is that although we center the map on the user's location, the map is not rotated to correspond to the device orientation. The Geolocation API unfortunately does not supply this information. Conveniently though, there is another useful HTML5 API, the Device Orientation API, which can provide such information. We'll revisit this example in a future article, and we'll see what happens when we add orientation data into the mix.

Browser support and fallbacks

As can be seen from the table below Geolocation is widely supported across the main mobile browsers, with the exception of Opera Mini.

AndroidiOSIE MobileOpera MobileOpera MiniFirefox MobileChrome for Android
       
(Sources: caniuse.comDeviceAtlasmobilehtml5.org)

It's also worth noting that in the examples above the code to use the Geolocation API in a webpage was quite simple. The situation is a little more complicated in reality. The value of coupling location-awareness with mobile services has long been acknowledged and a number of handset manufacturers and other organisations have flirted with geolocation support in the browser in the past. This has resulted in various different legacy geolocation implementations. These have included BONDI from the OMTP group, Palm, RIM, and Google Gears, some of which we've discussed previously here on mobiForge. If location is to be supported on these devices, then a web application needs to cater for these legacy APIs too.

Thankfully there are a couple of freely (under MIT license) available scripts that have done the hard work to insulate you from the details, including this script by Stan Wiechers, and this script by Esteban Acosta Villafañe which builds on Wiechers' script.

Taking a peek inside them, we can see that they check the device browser for support for any of the other legacy implementations as well as the W3C API, thus relieving you, the developer, of some of the drudgery!

That's it for now on the Geolocation API. Check back soon for the next article in our series, which will cover the Device Orientation API

Useful Links

Download

Download full code examples below.

AttachmentSize
GeolocationAPIExamples.zip2.09 KB
考虑到有人无法方位上面的例子,我把代码下载下来里面有三个html文件,附件中的例子:
geo1.html
<!DOCTYPE html>
<html>
  <head>
    <title>mobiForge geolocation static map demo</title>
    <style>
      #map {width:100%;height:100%;}
    </style>
    <script>
     
      function initGeolocation() {
        if (navigator && navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
        } else {
          console.log('Geolocation is not supported');
        }
      }
     
      function errorCallback() {}
     
      function successCallback(position) {
        var mapUrl = "http://maps.google.com/maps/api/staticmap?center=";
        mapUrl = mapUrl + position.coords.latitude + ',' + position.coords.longitude;
        mapUrl = mapUrl + '&zoom=15&size=512x512&maptype=roadmap&sensor=false';
        var imgElement = document.getElementById("static-map");
        imgElement.src = mapUrl;
      }
    </script>
  </head>
  <body οnlοad="javascript:initGeolocation()">
    <div id="map">
      <img id="static-map" src="blank.gif" />
    </div>
  </body>
</html>
geo2.html
<!DOCTYPE html>
<html>
  <head>
    <title>mobiForge geolocation map API demo</title>
    <style>
      body,html {height:100%}
      #map {width:100%;height:100%;}
    </style>
    <script src="http://maps.googleapis.com/maps/api/js?sensor=true"></script>
    <script>
     
      function initGeolocation() {
        if (navigator && navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
        } else {
          console.log('Geolocation is not supported');
        }
      }
     
      function errorCallback() {}

      function successCallback(position) {
        var myLatlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
        var map_options = {
          zoom: 15,
          center: myLatlng,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        }
       map_container = document.getElementById('map');
       var map = new google.maps.Map(map_container, map_options);
      }      
    </script>
  </head>
  <body οnlοad="javascript:initGeolocation()">
    <div id="map">
    </div>
  </body>
</html>

geo3.html
<!DOCTYPE html>
<html>
  <head>
    <title>mobiForge geolocation map API demo</title>
    <style>
      body,html {height:100%}
      #map {width:100%;height:100%;}
    </style>
    <script src="http://maps.googleapis.com/maps/api/js?sensor=true"></script>
    <script>
      var map; 
      function initGeolocation() {
        if (navigator && navigator.geolocation) {
        var watchId = navigator.geolocation.watchPosition(successCallback, 
                                                          errorCallback,
                                                          {enableHighAccuracy:true,timeout:60000,maximumAge:0});

        } else {
          console.log('Geolocation is not supported');
        }
      }
     
      function errorCallback() {}

      function successCallback(position) {
        var myLatlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
        if(map == undefined) {
          var myOptions = {
            zoom: 15,
            center: myLatlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
          }
          map = new google.maps.Map(document.getElementById("map"), myOptions);
        }
        else map.panTo(myLatlng);
    }


    </script>
  </head>
  <body οnlοad="javascript:initGeolocation()">
    <div id="map">
    </div>
  </body>
</html>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值