Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PhoneGap + Google Maps API v3 : Not displaying (at all) on Android

The Situation

I'm currently developing an app for a customer, who wishes to have a Google Maps integration. He wants the map to show the route from wherever the user is to his office.

I'm working on a Windows 8, without any IDE (using Sublime Text 2).

I've managed to get it working a) in my Chrome browser locally, b) in the Ripple Emulator for PhoneGap/Cordova >2.0.0. However, it simply will not work on my Android phone (HTC Sensation) whenever I try. It's driving me nuts and I'm just about to drop it and find some other, "dumber" solution (like a static map or the geo:url interface).

Before I tried to actually implement the map, I ran the the PhoneGap Geolocation full example, found here. I noted that my Android Phone did display my current position (lat/long/timestamp etc.) correctly. Thus, I believe that the correct permissions (Location -> etc.) has been set on my phone.

The Problem

Google Maps does not show up at all on my Android Device. I see the red background (for debugging), so I know the height and width are fine. But I do not see any sign of google maps (no buttons, overlays, grids or anything).

The Code

HTML code for loading jQuery, Cordova and Maps API v3:

<script type="text/javascript" src="js/jquery-1.10.0.min.js" ></script>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3&sensor=true&language=da"></script>

This is the HTML I use to place the map:

<div id="map-canvas"
    style="display:block;
            overflow:hidden;
            position:relative;
            border:1px solid #aaaaaa;
            width: 100%;
            height: 400px;
            background: red;">
</div>
<div id="map-panel" style="width:100%; height:90%; position:relative; "></div>

And here is my full Google Maps JS (in its own file):

var map,
    userPosition,
    officeLocation,
    directionsDisplay,
    directionsService;

google.maps.event.addDomListener(window, 'load', setup);

function setup() {
    document.addEventListener("deviceready", onDeviceReady, false);

    function onDeviceReady() {
        navigator.geolocation.getCurrentPosition(onSuccess, onError, {enableHighAccuracy:true});
    }
}

function onSuccess(position) {
    userPosition = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    navigator.notification.alert("Found user position");

    initializeMaps();
    //$('#map-canvas').css({'height': $(window).height()/2, 'width': '99%'});
}

function onError(error) {
    navigator.notification.alert("code: " + error.code + ",\n" +
                                 "message: " + error.message);
}

function initializeMaps() {
    directionsDisplay = new google.maps.DirectionsRenderer();
    directionsService = new google.maps.DirectionsService();

    officeLocation = new google.maps.LatLng(55.689403, 12.521281);

    var myOptions = {
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        center: officeLocation
    };

    map = new google.maps.Map(document.getElementById('map-canvas'), myOptions);
    directionsDisplay.setMap(map);

    if (userPosition != '') {
        var userPosMarker = new google.maps.Marker({
            position: userPosition,
            map: map,
            title: "Din Placering"
        });

        calculateRoute();
    }
    else {
        navigator.notification.alert("userPosition is null");
    }
}

function calculateRoute() {
    //navigator.notification.alert("calculateRoute");
    var request = {
        origin: userPosition,
        destination: officeLocation,
        travelMode: google.maps.DirectionsTravelMode["DRIVING"]
    };

    directionsService.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            directionsDisplay.setPanel(document.getElementById('map-panel'));
            directionsDisplay.setDirections(response);

            //navigator.notification.alert("Show directions");
        }
        else {
            navigator.notification.alert("Got status NOT OK from google");
        }
    });
}

function reloadGoogleMap() {
    if (map === null || map === undefined) {
        navigator.notification.alert("map is %s", map);
    }
    else {
        var currCenter = map.getCenter();
        google.maps.event.trigger(map, "resize");
        map.setCenter(currCenter);
        map.setZoom(12);
        //navigator.notification.alert("reloaded map");
    }
}

This is my initialization code (placed at the bottom of my head tag):

<script type="text/javascript" charset="utf-8">
    //navigator.notification.alert("listen for deviceready");
    document.addEventListener("deviceready", onDeviceReady, false);

    function onDeviceReady() {
        //navigator.notification.alert("device ready");
        ... 
        // calls other initialization functions
        ...
        initMaps();
        ..
    }
</script>

And I have these (among others) in my AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />

And I have these in my config.xml (located in /assets/www/config.xml):

<access origin="*" />

...

<feature name="http://api.phonegap.com/1.0/device"/>
<feature name="http://api.phonegap.com/1.0/geolocation"/>
<feature name="http://api.phonegap.com/1.0/notification"/>

It seems to my that my onSuccess method is never called, but I do not see any of the alerts stating that the user position is invalid. In fact, the only notification on my phone I do see is:

map is %s, undefined

In the ripple emulator I get the "Found user position" whenever I load the app.

Please help!

[EDIT] I forgot to mention that I'm using Adobe's http://build.phonegap.com to actually build the application.

[EDIT2] I now tried to use Google API key like so:

<script src="http://maps.google.com/maps/api/js?key=AIzaSyB1uhDdWjtNEl9K35lJtuq5Sw2BjKR8-OM&sensor=false" type="text/javascript"></script>

But alas, no change. Still nothing.

[EDIT3]

Here is my full androidManifest.xml:

<code>
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
                package="com.alphastagestudios.danmarksflyttemand" android:versionName="1.0" android:versionCode="2" android:hardwareAccelerated="true">
            <supports-screens
                    android:largeScreens="true"
                    android:normalScreens="true"
                    android:smallScreens="true"
                    android:xlargeScreens="true"
                    android:resizeable="true"
                    android:anyDensity="true"
                    />

            <uses-permission android:name="android.permission.CAMERA" />
            <uses-permission android:name="android.permission.VIBRATE" />
            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
            <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
            <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
            <uses-permission android:name="android.permission.INTERNET" />
            <uses-permission android:name="android.permission.RECEIVE_SMS" />
            <uses-permission android:name="android.permission.RECORD_AUDIO" />
            <uses-permission android:name="android.permission.RECORD_VIDEO"/>
            <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
            <uses-permission android:name="android.permission.READ_CONTACTS" />
            <uses-permission android:name="android.permission.WRITE_CONTACTS" />
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
            <uses-permission android:name="android.permission.GET_ACCOUNTS" />
            <uses-permission android:name="android.permission.BROADCAST_STICKY" />


            <application android:icon="@drawable/icon" android:label="@string/app_name"
                    android:hardwareAccelerated="true"
                    android:debuggable="true">
                    <activity android:name="DanmarksFlyttemandApp" android:label="@string/app_name"
                                    android:theme="@android:style/Theme.Black.NoTitleBar"
                                    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
                            <intent-filter>
                                    <action android:name="android.intent.action.MAIN" />
                                    <category android:name="android.intent.category.LAUNCHER" />
                            </intent-filter>
                    </activity>
            </application>

            <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17"/>

            <permission android:name="com.alphastagestudios.danmarksflyttemand.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
            <uses-permission android:name="com.alphastagestudios.danmarksflyttemand.permission.MAPS_RECEIVE"/>
    </manifest>

</code>

[EDIT4] I now tried building a minimal example with PhoneGap geolocation and a basic google map. I built it manually through Eclipse with Cordova v. 2.9.0 (the newest). Weird thing is, the PhoneGap geolocation example on its own worked fine, but when I introduce google maps code it all stopped working. I tried this minimal example with and without the google API key. No difference.

This is the index.html I was using:

<!DOCTYPE html>
<html>
  <head>
    <title>Device Properties Example</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">

    <style>
        body, html, #map-canvas {
            width: 100%;
            height: 400px;
            margin: 0;
            padding: 0;
        }
    </style>

    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
    <script type="text/javascript" charset="utf-8">

    // Wait for device API libraries to load
    document.addEventListener("deviceready", onDeviceReady, false);

    // device APIs are available
    function onDeviceReady() {
        navigator.geolocation.getCurrentPosition(onSuccess, onError);
    }

    // onSuccess Geolocation
    function onSuccess(position) {
        var element = document.getElementById('geolocation');
        element.innerHTML = 'Latitude: '           + position.coords.latitude              + '<br />' +
                            'Longitude: '          + position.coords.longitude             + '<br />' +
                            'Altitude: '           + position.coords.altitude              + '<br />' +
                            'Accuracy: '           + position.coords.accuracy              + '<br />' +
                            'Altitude Accuracy: '  + position.coords.altitudeAccuracy      + '<br />' +
                            'Heading: '            + position.coords.heading               + '<br />' +
                            'Speed: '              + position.coords.speed                 + '<br />' +
                            'Timestamp: '          + position.timestamp                    + '<br />';
        initMap();
    }

    // onError Callback receives a PositionError object
    function onError(error) {
        alert('code: '    + error.code    + '\n' +
              'message: ' + error.message + '\n');
    }

    var map,
        userPosition,
        officeLocation,
        directionsDisplay,
        directionsService;

    function initMap() {
        //directionsDisplay = new google.maps.DirectionsRenderer();
        //directionsService = new google.maps.DirectionsService();

        officeLocation = new google.maps.LatLng(55.689403, 12.521281);
        var myOptions = {
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            center: officeLocation
        };

        map = new google.maps.Map(document.getElementById('map-canvas'), myOptions);
        //directionsDisplay.setMap(map);
    }

    </script>

  </head>
  <body>
    <p id="geolocation">Finding geolocation...</p>

    <div id="map-canvas"></div>
  </body>
</html>

[EDIT5] I have now attempted to build the application with PhoneGap (Cordova)v. 2.6.0, 2.9.0 and 2.8.1 - none worked. The phone's geolocation works fine, but google maps does not show up. I only see the default grey background of where it should be.

like image 484
Rami Avatar asked Jul 02 '13 10:07

Rami


2 Answers

I had the same issue and it turned out to just be me not updating the whitelist after I created the phonegap project. Phonegap was rejecting the google maps JS URL, so it never got downloaded and executed.

For example:

 <access origin="*.google.com"/>
    <access origin="*.googleapis.com"/>
like image 102
Tim Avatar answered Oct 13 '22 14:10

Tim


I never solved this directly, but I started using Intel's App Framework, which had an example plugin for google maps (the original link is dead, see edit below). This plugin works great for me and I was able to add directions to it.

The code I ended up using is below. No permissions or any other adjustments needed. See the comments in the code. The map will show up regardless of geolocation success or error, but if it gets the current position it adds directions to the map. Otherwise it shows a static point.

// @author Ian Maffett
// @copyright App Framework 2012
// Modified by [email protected] - 2013
// - Added markers & directions

(function () {
    var gmapsLoaded = false; //internal variable to see if the google maps API is available

    //We run this on document ready.  It will trigger a gmaps:available event if it's ready
    // or it will include the google maps script for you
    $(document).ready(function () {
        if(window["google"]&&google.maps){
            $(document).trigger("gmaps:available");
            gmapsLoaded = true;
            return true;
        }
        var gmaps = document.createElement("script");
        gmaps.src = "http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true&callback=gmapsPluginLoaded";
        // Updated API url
        $("head").append(gmaps);
        window["gmapsPluginLoaded"] = function () {
            $(document).trigger("gmaps:available");
            gmapsLoaded = true;
        }
    });

    //Local cache of the google maps objects
    var mapsCache = {};

    //We can invoke this in two ways
    //If we pass in positions, we create the google maps object
    //If we do not pass in options, it returns the object
    // so we can act upon it.

    $.fn.gmaps = function (opts) {
        if (this.length == 0) return;
        if (!opts) return mapsCache[this[0].id];
        //Special resize event
        if(opts=="resize"&&mapsCache[this[0].id])
        {
            var map = mapsCache[this[0].id];
            var center = map.getCenter();
            google.maps.event.trigger(map, "resize");
            map.setCenter(center);
            map.setZoom(13);
            // Extended resize to recenter and reset zoom

            return map;
        }

        //loop through the items and create the new gmaps object
        for (var i = 0; i < this.length; i++) {
            new gmaps(this[i], opts);
        }
    };


    //This is a local object that gets created from the above.
    var gmaps = function (elem, opts) {
        var createMap = function () {
            var officePos = new google.maps.LatLng(55.689403, 12.521281);
            if (!opts || Object.keys(opts).length == 0) {
                opts = {
                    zoom: 13,
                    center: officePos,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                }
            }
            mapsCache[elem.id] = new google.maps.Map(elem, opts);

            // Added marker for static location 
            var officeMarker = new google.maps.Marker({
                position: officePos,
                map: mapsCache[elem.id],
                title: 'Danmarks Flyttemand ApS'
            });

            // Added custom event listener for availability of userPosition
            $(document).one('userPositionAvailable', function(evt, userPos) {
                if (userPos != null && userPos != '') {
                    addDirections(mapsCache[elem.id], userPos);
                }
            });
        }

        // Adds directions on gmap from userPos to a fixed position
        var addDirections = function(gmap, userPos) {
            var officePos = new google.maps.LatLng(55.689403, 12.521281); // fixed position
            var userMarker = new google.maps.Marker({
                icon: {
                    path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
                    strokeColor: "green",
                    scale: 5
                },
                position: userPos,
                map: gmap,
                title: 'Your location'
            });

            var directionsService = new google.maps.DirectionsService();
            var directionsDisplay = new google.maps.DirectionsRenderer({suppressMarkers: true});
            directionsDisplay.setMap(gmap);
            directionsDisplay.setPanel(document.getElementById('googledirections')); // add id here

            var request = {
                origin: userPos,
                destination: officePos,
                travelMode: google.maps.DirectionsTravelMode.DRIVING
            };
            directionsService.route(request, function(response, status) {
                if (status == google.maps.DirectionsStatus.OK) {
                    directionsDisplay.setDirections(response);
                }
            });
        }

        //If we try to create a map before it is available
        //listen to the event
        if (!gmapsLoaded) {
            $(document).one("gmaps:available", function () {
                createMap()
            });
        } else {
            createMap();
        }
    }
})(af);


function onGeoSuccess(position) {
    var userPos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    $(document).trigger('userPositionAvailable', userPos);
}

function onGeoError(error) {
    navigator.notification.alert('Geolocation error: ' + error.message);
}

function initMaps() {
    navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoError);

    var mapOptions = {
        zoom: 13,
        center: new google.maps.LatLng(55.689403, 12.521281),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    $('#googlemap').gmaps(mapOptions); // update id selector

    $('#googlemap-panel').on('loadpanel', function() { // update event for other framework and id selector
        $('#googlemap').gmaps('resize'); // update id selector
    });
}

[EDIT] It appears the original link for the example plugin is dead, so here is a quickly-found new link (untested and only skimmed quickly): New Google Maps Plugin Demo for Intel's App-Framework

like image 26
Rami Avatar answered Oct 13 '22 16:10

Rami