Actualité

Tutoriel Android : GLocation

Cela faisait quelques temps qu’on n’avait plus vu de tutoriel Android ! Aujourd’hui, André, un habitué du site, nous propose de découvrir l’utilisation de sa solution de géolocalisation grâce à un tutoriel complet. Attention, tutoriel intégralement en anglais ! Enjoy !

What you will learn :

  • How to create an Android application.
  • How to create menus.
  • How to broadcast and receive intents.
  • How to create a Map-Application.
  • How to use the LocationManager to track your Position.
  • How to draw things on the MapView using an OverlayController.
  • How to connect an Android application to a REST full service.
  • And more…

What it will look like :

  • View of the main screen of GlocationTutorial
  • View in satellite mode plus menu

Description :

  • First of all you must have a correct Android.xml .
    it is required to define activities. In our case, we have only one activity : GlocationMap which is the map view.
    To be able to access GPS informations we need also to define some permissions.

Android.xml

  • We need also to define resources for our project :

strings.xml

In our case, we don’t have to define a layout.

Next we have to copy the images files we need in a drawable folder under res folder.

  • Now we can work on android java code.
    Under src you will find the main classes group : com.android.maps which contain only MyMapView.java .
    This class extend MapView in a compatible way for traffic display as we plan to do.

MyMapView.java

package com.google.android.maps;
import android.content.Context;
import com.google.android.maps.MapView;
import com.google.common.DataRequestDispatcher;
import com.google.googlenav.map.Map;
import com.google.googlenav.map.TrafficService;
public class MyMapView extends MapView
{
public MyMapView(Context context)
{
super(context);
}
// We just adapt setup and to add a getMap method.
void setup(Map map, TrafficService traffic, DataRequestDispatcher dispatcher)
{
mMap = map;
super.setup(map, traffic, dispatcher);
}
public Map getMap()
{
return mMap;
}
private Map mMap;
}
  • We need now, to implement the MapActivity. In GlocationMap.java we use RequestQueue class to make the http call to GlocationServer. HttpConnection or HttpClient can also be used to make such a call. We use here RequestQueue because it is a non bloquing call. The request is in a queue and when RequestQueue get the server response it send an event to the event handler passed as parameter. In our case MyEventHandler3.

GlocationMap.java

package org.absynt.android.glocation;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentReceiver;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.http.RequestQueue;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MyMapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayController;
import com.google.android.maps.Point;
public class GlocationMap extends MapActivity {
// ===========================================================
// Fields
// ===========================================================
private static final String MY_LOCATION_CHANGED_ACTION = new String(
“android.intent.action.LOCATION_CHANGED”);
private static final String ACTION_SEND_TOAST = new String(
“android.intent.action.ACTION_SEND_TOAST”);
/**
* Minimum distance in meters for a followed to be recognize as a Followed
* to be drawn
*/

protected final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 25; // in Meters
protected final long MINIMUM_TIME_BETWEEN_UPDATE = 5000; // ms
protected final long MINIMUM_TIME_BETWEEN_UPDATE_LIST = 60000; // ms
/*
* Stuff we need to save as fields, because we want to use multiple of them
* in different methods.
*/

protected boolean doUpdates = true;
private MyMapView myMapView = null;
RequestQueue requestQueue;
private String loginId = “Guest”;
private String authToken = “freePass”;
public String m_baseurl = “”;
private int GET_MESSAGES = 2;
protected MapController myMapController = null;
protected OverlayController myOverlayController = null;
protected LocationManager myLocationManager = null;
protected Location myLocation = null;
protected MyIntentReceiver myIntentReceiver = null;
protected final IntentFilter myIntentFilter = new IntentFilter(
MY_LOCATION_CHANGED_ACTION);
protected boolean TRACK_TOOGLE_ACTION = false;
// Store these as global instances so we don’t keep reloading every time
public long endTime;
/**
* This tiny IntentReceiver updates our stuff as we receive the intents
* (LOCATION_CHANGED_ACTION) we told the myLocationManager to send to us.
*/

class MyIntentReceiver extends IntentReceiver {
@Override
public void onReceiveIntent(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MY_LOCATION_CHANGED_ACTION)) {
if (System.currentTimeMillis() > GlocationMap.this.endTime) {
GlocationMap.this.endTime = System.currentTimeMillis()
+ MINIMUM_TIME_BETWEEN_UPDATE_LIST;
GlocationMap.this.updateList();
// Will simply update our list,
// when receiving an intent

}
if (GlocationMap.this.doUpdates)
GlocationMap.this.updateView();
}
}
}
/**
* This method is so huge, because it does a lot of FANCY painting. We could
* shorten this method to a few lines. But as users like eye-candy apps 😉
* …
*/

protected class MyLocationOverlay extends Overlay {
private Paint innerPaint, borderPaint, textPaint;
Paint paint1 = new Paint();
Paint paint2 = new Paint();
@Override
public void draw(Canvas canvas, PixelCalculator calculator,
boolean shadow) {
super.draw(canvas, calculator, shadow);
// Setup our “brush”/”pencil”/ whatever…
Paint paint = new Paint();
paint2.setARGB(255, 255, 255, 255);
paint.setTextSize(14);
// Create a Point that represents our GPS-Location
Double lat = GlocationMap.this.myLocation.getLatitude() * 1E6;
Double lng = GlocationMap.this.myLocation.getLongitude() * 1E6;
Point point = new Point(lat.intValue(), lng.intValue());
int[] myScreenCoords = new int[2];
// Converts lat/lng-Point to OUR coordinates on the screen.
calculator.getPointXY(point, myScreenCoords);
// Draw a circle for our location
RectF oval = new RectF(myScreenCoords[0] – 7,
myScreenCoords[1] + 7, myScreenCoords[0] + 7,
myScreenCoords[1] – 7);
// Setup a color for our location
// paint.setStyle(Style.FILL);
paint.setStyle(Style.STROKE);
paint.setARGB(255, 80, 150, 30); // Nice strong Android-Green
// Draw our name
canvas.drawText(getString(R.string.map_overlay_own_name),
myScreenCoords[0] + 9, myScreenCoords[1], paint);
// Change the paint to a ‘Lookthrough’ Android-Green
paint.setARGB(80, 156, 192, 36);
paint.setStrokeWidth(1);
// draw an oval around our location
canvas.drawOval(oval, paint);
// With a black stroke around the oval we drew before.
paint.setARGB(255, 0, 0, 0);
paint.setStyle(Style.STROKE);
canvas.drawCircle(myScreenCoords[0], myScreenCoords[1], 7, paint);
}
public Paint getInnerPaint()
{
if (innerPaint == null)
{
innerPaint = new Paint();
innerPaint.setARGB(225, 75, 75, 75); // gray
innerPaint.setAntiAlias(true);
}
return innerPaint;
}
public Paint getBorderPaint()
{
if (borderPaint == null)
{
borderPaint = new Paint();
borderPaint.setARGB(255, 255, 255, 255);
borderPaint.setAntiAlias(true);
borderPaint.setStyle(Style.STROKE);
borderPaint.setStrokeWidth(2);
}
return borderPaint;
}
public Paint getTextPaint()
{
if (textPaint == null)
{
textPaint = new Paint();
textPaint.setARGB(255, 255, 255, 255);
textPaint.setAntiAlias(true);
}
return textPaint;
}
}
private void updateList()
{
// Refresh our location…
this.myLocation = myLocationManager.getCurrentLocation(“gps”);
Map headers = new HashMap();
try
{
String text = URLEncoder.encode(myLocation.toString(), “UTF-8”);
Log.e(“Glocation”, GlocationMap.this.m_baseurl
+ “/GlocationServer/?action=location&location=” + text
+ “&loginId=” + loginId + “&authToken=” + authToken);
// We send our location to GlocationServer
requestQueue
.queueRequest(GlocationMap.this.m_baseurl
+ “/GlocationServer/?action=location&location=” + text
+ “&loginId=” + loginId + “&authToken=” + authToken,
“POST”, headers,
new MyEventHandler3(GlocationMap.this), null, 0, false);
}
catch (UnsupportedEncodingException e)
{
Log.e(“GlocationMap”, e.getMessage());
}
}
// We create a method sending an Intent to write Toast.
// This will allow us in the next tutorial to display message coming from this Map/Activity
// in another Activity

public void writeToast(String msg)
{
Intent intent = new Intent(ACTION_SEND_TOAST);
intent.putExtra(“toast”, msg);
broadcastIntent(intent);
}
public MyMapView getMMapView()
{
return GlocationMap.this.myMapView;
}
// ===========================================================
// “””Constructor””” (or the Entry-Point of this class)
// ===========================================================

@Override
protected void onCreate(Bundle icicle)
{
super.onCreate(icicle);
/* Create a new MapView and show it */
GlocationMap.this.m_baseurl = (String) getText(R.string.base_url);
GlocationMap.this.myMapView = new MyMapView(this);
this.setContentView(GlocationMap.this.myMapView);
requestQueue = new RequestQueue(this);
/*
* MapController is capable of zooming and animating and stuff like that
*/

this.myMapController = GlocationMap.this.myMapView.getController();
/*
* With these objects we are capable of drawing graphical stuff on top
* of the map
*/

this.myOverlayController = GlocationMap.this.myMapView
.createOverlayController();
MyLocationOverlay myLocationOverlay = new MyLocationOverlay();
this.myOverlayController.add(myLocationOverlay, true);
// Initialize the LocationManager
this.myLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
/*
* Prepare the things, that will give us the ability, to receive
* Information about our GPS-Position.
*/

this.setupForGPSAutoRefreshing();
this.myLocation = myLocationManager.getCurrentLocation(“gps”);
Double lat = GlocationMap.this.myLocation.getLatitude() * 1E6;
Double lng = GlocationMap.this.myLocation.getLongitude() * 1E6;
Point point = new Point(lat.intValue(), lng.intValue());
this.myMapController.centerMapTo(point, false);
this.myMapController.zoomTo(11);
this.updateView();
}
@Override
public void onPause()
{
super.onPause();
this.doUpdates = false;
}
@Override
public void onStop()
{
super.onStop();
}
@Override
public void onRestart()
{
super.onRestart();
this.doUpdates = true;
}
@Override
public void onDestroy()
{
super.onDestroy();
this.unregisterReceiver(this.myIntentReceiver);
}
/**
* Restart the receiving, when we are back on line.
*/

@Override
public void onResume()
{
super.onResume();
this.doUpdates = true;
}
/**
* Make sure to stop the animation when we’re no longer on screen, failing
* to do so will cause a lot of unnecessary cpu-usage!
*/

@Override
public void onFreeze(Bundle icicle)
{
super.onFreeze(icicle);
this.doUpdates = false;
}
// ===========================================================
// Overridden methods
// ===========================================================
// Called only the first time the options menu is displayed.
// Create the menu entries.
// Menu adds items in the order shown.

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
boolean supRetVal = super.onCreateOptionsMenu(menu);
menu.add(0, 0, getString(R.string.main_menu_zoom_in));
menu.add(0, 1, getString(R.string.main_menu_zoom_out));
menu.add(0, 2, getString(R.string.main_menu_toggle_street_satellite));
menu.add(0, 4, getString(R.string.main_menu_traffic));
menu.add(0, 8, getString(R.string.main_menu_toogleTrackMode));
return supRetVal;
}
@Override
public boolean onOptionsItemSelected(Menu.Item item)
{
switch (item.getId())
{
case 0:
// Zoom not closer than possible
this.myMapController.zoomTo(Math.min(21, GlocationMap.this.myMapView.getZoomLevel() + 1));
return true;
case 1:
// Zoom not farer than possible
this.myMapController.zoomTo(Math.max(1, GlocationMap.this.myMapView.getZoomLevel() – 1));
return true;
case 2:
// Switch to satellite view
GlocationMap.this.myMapView.toggleSatellite();
return true;
case 4:
// Switch on traffic overlays
GlocationMap.this.myMapView.toggleTraffic();
return true;
case 8:
if (TRACK_TOOGLE_ACTION)
{
GlocationMap.this.TRACK_TOOGLE_ACTION = false;
}
else
{
GlocationMap.this.TRACK_TOOGLE_ACTION = true;
}
return true;
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_I)
{
// Zoom not closer than possible
this.myMapController.zoomTo(Math.min(21,
GlocationMap.this.myMapView.getZoomLevel() + 1));
return true;
}
else if (keyCode == KeyEvent.KEYCODE_O)
{
// Zoom not farer than possible
this.myMapController.zoomTo(Math.max(1, GlocationMap.this.myMapView.getZoomLevel() – 1));
return true;
}
else if (keyCode == KeyEvent.KEYCODE_S)
{
// Switch on the satellite images
GlocationMap.this.myMapView.toggleSatellite();
return true;
} else if (keyCode == KeyEvent.KEYCODE_T)
{
// Switch on traffic overlays
GlocationMap.this.myMapView.toggleTraffic();
return true;
} else if (keyCode >= KeyEvent.KEYCODE_1
&& keyCode <= KeyEvent.KEYCODE_9)
{
int item = keyCode – KeyEvent.KEYCODE_1;
Log.e(“GlocationMAP”, “MyLocationOverlay…KEYCODE_1 ” + item);
} else if (keyCode == KeyEvent.KEYCODE_C)
{
if (TRACK_TOOGLE_ACTION)
{
GlocationMap.this.TRACK_TOOGLE_ACTION = false;
}
else
{
GlocationMap.this.TRACK_TOOGLE_ACTION = true;
}
// this.myMapController.setFollowMyLocation(TRACK_TOOGLE_ACTION);
return true;
}
return false;
}
// ===========================================================
// Methods
// ===========================================================
private void setupForGPSAutoRefreshing() {
/*
* Register with out LocationManager to send us an intent (whos
* Action-String we defined above) when an intent to the location
* manager, that we want to get informed on changes to our own position.
* This is one of the hottest features in Android.
*/

List providers = this.myLocationManager
.getProviders();
// Get the first provider available
LocationProvider provider = providers.get(0);
this.myLocationManager.requestUpdates(provider,
MINIMUM_TIME_BETWEEN_UPDATE, MINIMUM_DISTANCECHANGE_FOR_UPDATE,
new Intent(MY_LOCATION_CHANGED_ACTION));
/*
* Create an IntentReceiver, that will react on the Intents we made our
* LocationManager to send to us.
*/

this.myIntentReceiver = new MyIntentReceiver();
this.registerReceiver(this.myIntentReceiver, this.myIntentFilter);
}
private void updateView() {
// Refresh our gps-location
this.myLocation = myLocationManager.getCurrentLocation(“gps”);
if (TRACK_TOOGLE_ACTION) {
Double lat = GlocationMap.this.myLocation.getLatitude() * 1E6;
Double lng = GlocationMap.this.myLocation.getLongitude() * 1E6;
Point point = new Point(lat.intValue(), lng.intValue());
this.myMapController.centerMapTo(point, false);
}
/*
* Redraws the mapView, which also makes our OverlayController redraw
* our Circles and Lines
*/

GlocationMap.this.myMapView.invalidate();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
String data, Bundle extras) {
if (requestCode == GET_MESSAGES) {
GlocationMap.this.updateView();
}
}
}
  • Now we need to code MyEventHandler3 used in our code in RequestQueue call. In that case it is very easy :

MyEventHandler3.java

package org.absynt.android.glocation;
import android.app.Activity;
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.net.http.SslCertificate;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
/**
* Handler to get the http status when we post a new tweet
*/

class MyEventHandler3 implements EventHandler
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Activity activity;
private GlocationMap glocationMap;
MyEventHandler3(Activity activity)
{
this.activity = activity;
this.glocationMap = (GlocationMap) activity;
}
public void status(int i, int i1, int i2, String s)
{
Log.d(“MyEventHandler3”, “status [” + s + “]”);
}
@SuppressWarnings(“unchecked”)
public void headers(Iterator iterator)
{
}
public void headers(Headers headers)
{
}
public void data(byte[] bytes, int len)
{
baos.write(bytes, 0, len);
}
public void endData()
{
// String text = new String(baos.toByteArray());
// Log.e(“MyEventHandler3”, “text = ” + text);

}
public void error(int i, String s)
{
Log.e(“MyEventHandler3”, “error [” + s + “]”);
glocationMap.writeToast(“MyEventHandler3 error [” + s + “]”);
}
public void handleSslErrorRequest(int i, String s, SslCertificate sslCertificate)
{
Log.e(“MyEventHandler3”, “ssl error [” + s + “]”);
glocationMap.writeToast(“MyEventHandler3 error [” + s + “]”);
}
}

Merci André pour ce tutoriel… La suite au prochain épisode !

Edit : Voici enfin les sources du tutoriel. Enjoy !

Related Articles

Samsung Galaxy S : La mise à jour Android 2.3 Gingerbread arrive

Olivier

Mise à jour : l’Android Lollipop n’est pas sortie de l’auberge

Olivier

Le Motorola Milestone fait aussi sa pub au Royaume-Uni

Olivier

80 comments

Leave a Comment