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

HTC Dream Android chez Orange : Communiqué de presse

Olivier

Toshiba lance le TransferJet qui permet d’augmenter la vitesse de transfert entre Smartphone et PC

Frederic

Des changements dans l’algorithme de classement de l’Android Market?

Olivier

80 comments