| Author |
Message |
jeremian Freshman

Joined: 13 Dec 2007 Posts: 6
|
Posted: Thu Dec 27, 2007 10:41 pm Post subject: TrackBuilder for mock location providers |
|
|
TrackBuilder for mock location providers
What is this: This tutorial shows how to generate the track file for mock providers.
What you will learn:
- How to open files and write to them.
- How to display alerts from different threads
- How to use submenus
- How to transfer complex data between sub-activities
Problems/Questions:
- Is it possible to manipulate files in /data/misc/location/ directly from the application?
- Is it possible to 'reset' location providers?
Difficulty: 3 of 5
Usage:
1): We have to create the path. New points are added by pressing 'P' (or choosing the appropriate option from the menu).
2): After clicking 'Save track' (or pressing 'X') the new sub-activity is displayed. We have to enter the filename and speed of the recorded track (in microdegrees per second). I think 20 is a quick walk.
3): The track file is saved as 'filename_track' in the application's files directory.
4): We have to use adb in order to copy our track file to /data/misc/location/ directory. Here comes a small example, where I create the provider named 'test1' with the previously written track file.
a) We have to create /data/misc/location/test1 directory.
| Code: |
adb shell
# cd /data/misc/location
# mkdir test1
# exit
|
b) We have to download the track file from the emulator.
| Code: |
adb pull /data/data/net.mobilefight.trackbuilder/files/test1_track track
|
c) We have to upload the track file to /data/misc/location/test1/ directory.
| Code: |
adb push track /data/misc/location/test1/track
|
5): We can check the created track by choosing it from the 'GPS providers' submenu.
6): The violet dot is the location received from provider.
Description:
Because most of the code is taken from other tutorials from this site, I have decided to attach only full sources without detailed step-by-step description. However, if you have any questions, don't hesitate to ask. Any ideas will be welcomed.
The full source:
"/res/layout/main.xml":
| XML: |
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World, TrackBuilder!"
/>
</LinearLayout>
|
"/res/layout/save_file.xml":
| XML: |
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dip">
<TextView
id="@+id/save_points_label1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/save_points_label1"
/>
<EditText
id="@+id/save_points_edit_returnvalue"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:singleLine="true"
android:layout_below="@id/save_points_label1"
/>
<TextView
id="@+id/save_points_label2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/save_points_label2"
android:layout_below="@id/save_points_edit_returnvalue"
/>
<EditText
id="@+id/save_points_edit_step"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:singleLine="true"
android:layout_below="@id/save_points_label2"
android:drawSelectorOnTop="true"
android:numeric="true"
android:text="@string/save_points_default_step"
/>
<Button
id="@+id/save_points_cmd_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/save_points_edit_step"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="@string/save_points_cmd_cancel"
/>
<Button
id="@+id/save_points_cmd_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeft="@id/save_points_cmd_cancel"
android:layout_alignTop="@id/save_points_cmd_cancel"
android:text="@string/save_points_cmd_save"
/>
</RelativeLayout>
|
"/res/layout/file_row.xml":
| XML: |
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
|
"/res/values/strings.xml":
| XML: |
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">TrackBuilder</string>
<string name="map_menu_zoom_in">Zoom in (Key: I)</string>
<string name="map_menu_zoom_out">Zoom out (Key: O)</string>
<string name="map_menu_exit">Exit</string>
<string name="map_menu_satellite">Toggle Satellite (Key: S)</string>
<string name="map_menu_traffic">Toggle Traffic (Key: T)</string>
<string name="map_menu_set_point">Set point (Key: P)</string>
<string name="map_menu_remove_point">Remove last point (Key: R)</string>
<string name="map_menu_clear_points">Clear all points (Key: C)</string>
<string name="map_menu_providers">GPS providers</string>
<string name="map_menu_save_points">Save track (Key: X)</string>
<string name="map_menu_save_path">Save path</string>
<string name="map_menu_load_path">Load path</string>
<string name="map_submenu_none">none</string>
<string name="save_points_name">Save track</string>
<string name="save_points_cmd_save">Save</string>
<string name="save_points_cmd_cancel">Cancel</string>
<string name="save_points_default_step">20</string>
<string name="save_points_label1">Filename:</string>
<string name="save_points_label2">Step (in microdegrees):</string>
<string name="save_path_name">Save path</string>
<string name="load_path_name">Load path</string>
</resources>
|
"AndroidManifest.xml":
| XML: |
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.mobilefight.trackbuilder">
<application android:icon="@drawable/icon">
<activity class=".TrackBuilder" android:label="@string/app_name">
<intent-filter>
<action android:value="android.intent.action.MAIN" />
<category android:value="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:value="android.intent.action.VIEW" />
<category android:value="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity class=".SavePoints" android:label="@string/save_points_name" />
<activity class=".LoadPath" android:label="@string/load_path_name" />
</application>
</manifest>
|
"/src/your_package_structure/TrackBuilder.java":
| Java: |
package net.mobilefight.trackbuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayController;
import com.google.android.maps.Point;
import android.app.NotificationManager;
import android.app.ProgressDialog;
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.os.Bundle;
import android.os.Looper;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.SubMenu;
public class TrackBuilder extends MapActivity {
protected LocationManager myLocationManager = null;
protected Location myLocation = null;
protected MyIntentReceiver myIntentReceiver = new MyIntentReceiver();
protected final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 5; // in Meters
protected final long MINIMUM_TIME_BETWEEN_UPDATE = 1000; // in Milliseconds
protected static final String LOCATION_CHANGED_ACTION =
new String("android.intent.action.LOCATION_CHANGED");
protected final IntentFilter myIntentFilter = new IntentFilter(LOCATION_CHANGED_ACTION);
protected final Intent myIntent = new Intent(LOCATION_CHANGED_ACTION);
protected boolean doUpdates = true;
protected ProgressDialog myProgressDialog;
protected MapView myMapView = null;
protected MapController myMapController = null;
protected OverlayController myOverlayController = null;
protected List<Location> path = new ArrayList<Location>();
protected SubMenu providers;
protected LocationProvider provider;
protected static final int SAVE_POINTS_REQUEST_CODE = 666;
protected static final int SAVE_PATH_REQUEST_CODE = 667;
protected static final int LOAD_PATH_REQUEST_CODE = 668;
protected boolean providersLoaded = false;
protected boolean loadingProviders = false;
protected boolean loadedPath = false;
class MyIntentReceiver extends IntentReceiver {
@Override
public void onReceiveIntent(Context context, Intent intent) {
if(TrackBuilder.this.doUpdates)
TrackBuilder.this.updateView();
}
}
protected class MyLocationOverlay extends Overlay {
@Override
public void draw(Canvas canvas, PixelCalculator calculator, boolean shadow) {
super.draw(canvas, calculator, shadow);
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setARGB(255, 105, 105, 105);
Point mapCentre = myMapView.getMapCenter();
// we are always drawing the long/lat of the mapCentre
canvas.drawText("longitude: " + mapCentre.getLongitudeE6() +
", latitude: " + mapCentre.getLatitudeE6(),
5, 15, paint);
// if there is GPS provider available, we draw the violet dot at the current
// position and the long/lat at the top of the screen
if (doUpdates && myLocation != null && provider != null) {
Double lat = TrackBuilder.this.myLocation.getLatitude() * 1E6;
Double lng = TrackBuilder.this.myLocation.getLongitude() * 1E6;
Point point = new Point(lat.intValue(), lng.intValue());
int[] myScreenCoords = new int[2];
calculator.getPointXY(point, myScreenCoords);
paint.setARGB(255, 80, 30, 150);
canvas.drawOval(new RectF(myScreenCoords[0] - 5, myScreenCoords[1] + 5,
myScreenCoords[0] + 5, myScreenCoords[1] - 5), paint);
canvas.drawText("longitude: " + point.getLongitudeE6() +
", latitude: " + point.getLatitudeE6(),
5, 26, paint);
}
int[] prevScreenCoords = new int[2];
boolean first = true;
// we are iterating all the path's points and draw the green dots at the
// points' positions and red lines between them
for (Location loc : path) {
Point point = new Point((int) loc.getLatitude(), (int) loc.getLongitude());
int[] screenCoords = new int[2];
calculator.getPointXY(point, screenCoords);
paint.setARGB(80, 156, 192, 36);
canvas.drawOval(new RectF(screenCoords[0] - 5, screenCoords[1] + 5,
screenCoords[0] + 5, screenCoords[1] - 5), paint);
if (!first) {
paint.setARGB(80, 255, 0, 0);
canvas.drawLine(prevScreenCoords[0], prevScreenCoords[1],
screenCoords[0], screenCoords[1], paint);
}
prevScreenCoords[0] = screenCoords[0];
prevScreenCoords[1] = screenCoords[1];
first = false;
}
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
myLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
myMapView = new MapView(this);
setContentView(myMapView);
myMapController = this.myMapView.getController();
myOverlayController = this.myMapView.createOverlayController();
MyLocationOverlay myLocationOverlay = new MyLocationOverlay();
myOverlayController.add(myLocationOverlay, true);
myMapController.zoomTo(2);
}
@Override
public void onResume() {
super.onResume();
doUpdates = true;
registerReceiver(myIntentReceiver, myIntentFilter);
}
@Override
public void onFreeze(Bundle icicle) {
doUpdates = false;
unregisterReceiver(myIntentReceiver);
super.onFreeze(icicle);
}
private void updateView() {
if (provider != null) {
myLocation = myLocationManager.getCurrentLocation(provider.getName());
myMapView.invalidate();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean supRetVal = super.onCreateOptionsMenu(menu);
menu.add(0, 0, getString(R.string.map_menu_zoom_in));
menu.add(0, 1, getString(R.string.map_menu_zoom_out));
menu.add(0, 2, getString(R.string.map_menu_satellite));
menu.add(0, 3, getString(R.string.map_menu_traffic));
menu.add(0, 4, getString(R.string.map_menu_set_point));
menu.add(0, 5, getString(R.string.map_menu_remove_point));
menu.add(0, 6, getString(R.string.map_menu_clear_points));
// we remember the reference to submenu in order to modify it in the future
providers = menu.addSubMenu(0, 7, getString(R.string.map_menu_providers));
menu.add(0, 8, getString(R.string.map_menu_save_points));
menu.addSeparator(0, 9);
menu.add(0, 10, getString(R.string.map_menu_save_path));
menu.add(0, 11, getString(R.string.map_menu_load_path));
menu.addSeparator(0, 12);
menu.add(0, 13, getString(R.string.map_menu_exit));
return supRetVal;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
final String data, final Bundle extras) {
super.onActivityResult(requestCode, resultCode, data, extras);
switch (requestCode) {
case SAVE_POINTS_REQUEST_CODE: // this is the result from save points subactivity
if (resultCode == RESULT_OK) {
// display the progress dialog
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Saving track...", true);
// start the thread, which will do the hard work
new Thread() {
public void run() {
boolean resultOK = TrackFactory.saveFile(path, TrackBuilder.this, data, extras.getInteger("step"));
myProgressDialog.dismiss();
// we have to call Looper.prepare() before calling showAlert
Looper.prepare();
if (resultOK) {
showAlert("File saved", "Track successfully saved as " + data, "OK", false);
}
else {
showAlert("Error", "Track cannot be saved as " + data, "OK", false);
}
// in order to display alert dialog we have to call Looper.loop()
Looper.loop();
// after Looper.loop() we have to call quit() on our looper
Looper.myLooper().quit();
}
}.start();
}
break;
case SAVE_PATH_REQUEST_CODE:
if (resultCode == RESULT_OK) {
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Saving path...", true);
new Thread() {
public void run() {
boolean resultOK = TrackFactory.savePath(path, TrackBuilder.this, data);
myProgressDialog.dismiss();
Looper.prepare();
if (resultOK) {
showAlert("File saved", "Path successfully saved as " + data, "OK", false);
}
else {
showAlert("Error", "Path cannot be saved as " + data, "OK", false);
}
Looper.loop();
Looper.myLooper().quit();
}
}.start();
}
break;
case LOAD_PATH_REQUEST_CODE:
if (resultCode == RESULT_OK) {
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Loading path...", true);
loadedPath = false;
new Thread() {
public void run() {
List<Location> newPath = new ArrayList<Location>();
boolean resultOK = TrackFactory.loadPath(newPath, TrackBuilder.this, data);
myProgressDialog.dismiss();
Looper.prepare();
if (resultOK) {
path.clear();
path.addAll(newPath);
showAlert("File loaded", "Path file " + data + " successfully loaded", "OK", false);
}
else {
showAlert("Error", "Path file " + data + " cannot be loaded", "OK", false);
}
// we have to release the main thread before calling Looper.lopp()!
loadedPath = true;
Looper.loop();
Looper.myLooper().quit();
}
}.start();
// we have to wait on the worker thread to invalidate the map view at the
// proper moment
while (true) {
if (loadedPath) {
break;
}
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
// do nothing
}
}
// invalidate the map view in order to redraw the screen
myMapView.invalidate();
}
else if (resultCode == LoadPath.RESULT_EMPTY) {
showAlert("Error", "There are no saved path files!", "OK", false);
}
break;
}
}
@Override
public boolean onOptionsItemSelected(Menu.Item item){
if (item.getGroup() == 0) { // main menu
switch (item.getId()) {
case 0: return zoomIn();
case 1: return zoomOut();
case 2: return toggleSatellite();
case 3: return toggleTraffic();
case 4: return setPoint();
case 5: return removePoint();
case 6: return clearAllPoints();
case 7: return updateProviders();
case 8: return savePoints();
case 10: return savePath();
case 11: return loadPath();
case 13: return exit();
}
}
else { // submenu
if (item.getId() == 0) { // none
myLocationManager.removeUpdates(myIntent);
doUpdates = false;
myLocation = null;
provider = null;
myMapView.invalidate();
}
else {
List<LocationProvider> locProviders = myLocationManager.getProviders();
provider = locProviders.get(item.getId()-1);
myLocationManager.requestUpdates(provider, MINIMUM_TIME_BETWEEN_UPDATE,
MINIMUM_DISTANCECHANGE_FOR_UPDATE, myIntent);
onResume();
}
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_I: return zoomIn();
case KeyEvent.KEYCODE_O: return zoomOut();
case KeyEvent.KEYCODE_S: return toggleSatellite();
case KeyEvent.KEYCODE_T: return toggleTraffic();
case KeyEvent.KEYCODE_P: return setPoint();
case KeyEvent.KEYCODE_R: return removePoint();
case KeyEvent.KEYCODE_C: return clearAllPoints();
case KeyEvent.KEYCODE_X: return savePoints();
}
return false;
}
// menu methods
private boolean zoomIn() {
this.myMapController.zoomTo(Math.min(21, this.myMapView.getZoomLevel() + 1));
return true;
}
private boolean zoomOut() {
this.myMapController.zoomTo(Math.max(1, this.myMapView.getZoomLevel() - 1));
return true;
}
private boolean toggleSatellite() {
myMapView.toggleSatellite();
return true;
}
private boolean toggleTraffic() {
myMapView.toggleTraffic();
return true;
}
private boolean setPoint() {
Point mapCentre = myMapView.getMapCenter();
Location centreLocation = new Location();
centreLocation.setLatitude(mapCentre.getLatitudeE6());
centreLocation.setLongitude(mapCentre.getLongitudeE6());
path.add(centreLocation);
myMapView.invalidate();
return true;
}
private boolean removePoint() {
if (!path.isEmpty()) {
path.remove(path.size()-1);
myMapView.invalidate();
}
return true;
}
private boolean exit() {
this.finish();
return true;
}
private boolean updateProviders() {
synchronized (this) {
if (providersLoaded) {
// the worker thread has finished and we simply update the
// providers sumbenu with retrieved providers
providers.removeGroup(1);
providers.add(1, 0, getString(R.string.map_submenu_none));
Collection<LocationProvider> locProviders = myLocationManager.getProviders();
int counter = 1;
for (LocationProvider provider : locProviders) {
providers.add(1, counter, provider.getName());
counter++;
}
return true;
}
else if (loadingProviders) { // the working thread hasn't finished yet
return true;
}
// we have to set this flag at this moment to avoid creation of multiple threads
loadingProviders = true;
}
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Retrieving available providers...", true);
new Thread() {
// this working thread simply calls getProviders() method in order
// to initialize the providers
public void run() {
myLocationManager.getProviders();
myProgressDialog.dismiss();
synchronized (TrackBuilder.this) {
providersLoaded = true;
}
}
}.start();
return true;
}
private boolean clearAllPoints() {
path.clear();
this.myMapView.invalidate();
return true;
}
private boolean savePoints() {
if (path.size() < 2) {
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(100, "You have to put at least two points on the map!",
NotificationManager.LENGTH_SHORT, null);
}
else {
// start save points subactivity
Intent i = new Intent(TrackBuilder.this, SavePoints.class);
// we are adding the bundle to the intent with 'path' set to false
Bundle extras = new Bundle();
extras.putBoolean("path", false);
i.putExtras(extras);
startSubActivity(i, SAVE_POINTS_REQUEST_CODE);
}
return true;
}
private boolean savePath() {
if (path.size() < 2) {
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(100, "You have to put at least two points on the map!",
NotificationManager.LENGTH_SHORT, null);
}
else {
Intent i = new Intent(TrackBuilder.this, SavePoints.class);
// we are adding the bundle to the intent with 'path' set to true
Bundle extras = new Bundle();
extras.putBoolean("path", true);
i.putExtras(extras);
startSubActivity(i, SAVE_PATH_REQUEST_CODE);
}
return true;
}
private boolean loadPath() {
Intent i = new Intent(TrackBuilder.this, LoadPath.class);
startSubActivity(i, LOAD_PATH_REQUEST_CODE);
return true;
}
}
|
"/src/your_package_structure/TrackFactory.java":
| Java: |
package net.mobilefight.trackbuilder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import com.google.android.maps.Point;
import android.location.Location;
public class TrackFactory {
private static long time = 0;
public static double euclDistance(Point p1, Point p2) {
return Math.sqrt(((double)p1.getLatitudeE6()-p2.getLatitudeE6())*((double)p1.getLatitudeE6()-p2.getLatitudeE6())
+ ((double)p1.getLongitudeE6()-p2.getLongitudeE6())*((double)p1.getLongitudeE6()-p2.getLongitudeE6()));
}
private static double genStepSmallPoints(double fstep, Point start, Point end, final double step, List<Point> computed) {
double dist = euclDistance(start, end);
if (fstep > dist) {
return fstep - dist;
}
double t = fstep / dist;
computed.add(new Point((int) (start.getLatitudeE6() * (1 - t) + end.getLatitudeE6() * t),
(int) (start.getLongitudeE6() * (1 - t) + end.getLongitudeE6() * t)));
int points_no = (int)((dist-fstep)/step);
for (int i = 0; i < points_no; ++i) {
t += step / dist;
computed.add(new Point((int) (start.getLatitudeE6() * (1 - t) + end.getLatitudeE6() * t),
(int) (start.getLongitudeE6() * (1 - t) + end.getLongitudeE6() * t)));
}
return step - (dist - fstep - (points_no*step));
}
public static List<Point> genAllSteps(List<Location> big_points, double step) {
List<Point> ret = new Vector<Point>();
double fstep = 0.0;
for (int i = 0; i < big_points.size() - 1; ++i) {
Location loc = big_points.get(i);
Point pointI = new Point((int) loc.getLatitude(), (int) loc.getLongitude());
loc = big_points.get(i+1);
Point pointIp1 = new Point((int) loc.getLatitude(), (int) loc.getLongitude());
fstep = genStepSmallPoints(fstep, pointI, pointIp1, step, ret);
}
return ret;
}
private static String prepareTrackSequence(Point point) {
StringBuilder toReturn = new StringBuilder();
toReturn.append(time);
toReturn.append(" ");
toReturn.append((double)point.getLongitudeE6() / 1E6);
toReturn.append(" ");
toReturn.append((double)point.getLatitudeE6() / 1E6);
toReturn.append(" 0");
time += 1000;
return toReturn.toString();
}
public final static boolean saveFile(List<Location> path, TrackBuilder context, String filename, int step) {
try {
BufferedOutputStream output = new BufferedOutputStream(context.openFileOutput(filename + "_track", 0));
List<Point> points = genAllSteps(path, step);
for (Point point : points) {
output.write((prepareTrackSequence(point) + "\n").getBytes());
}
output.close();
} catch (IOException e) {
return false;
}
return true;
}
public final static boolean savePath(List<Location> path, TrackBuilder context, String filename) {
try {
BufferedOutputStream output = new BufferedOutputStream(context.openFileOutput(filename + "_path", 0));
for (Location loc : path) {
output.write((loc.getLongitude() + " " + loc.getLatitude() + "\n").getBytes());
}
output.close();
} catch (IOException e) {
return false;
}
return true;
}
public final static boolean loadPath(List<Location> path, TrackBuilder context, String filename) {
try {
BufferedInputStream input = new BufferedInputStream(context.openFileInput(filename));
StringBuilder data = new StringBuilder();
byte[] buffer = new byte[4096];
while (input.read(buffer) != -1) {
data.append(new String(buffer));
}
StringTokenizer tokenizer = new StringTokenizer(data.toString(), " \n");
try {
while (tokenizer.hasMoreTokens()) {
Location location = new Location();
location.setLongitude(Double.valueOf(tokenizer.nextToken()));
location.setLatitude(Double.valueOf(tokenizer.nextToken()));
path.add(location);
}
}
catch (Exception e) {
// do nothing
}
input.close();
} catch (IOException e) {
return false;
}
return true;
}
}
|
"/src/your_package_structure/SavePoints.java":
| Java: |
package net.mobilefight.trackbuilder;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class SavePoints extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.save_file);
// we are checking the bundle attached to intent for the 'path' value.
// if it's true, we have to change the activity title and hide label
// end edit widget for step value
if (getIntent().getExtras().getBoolean("path")) {
setTitle(getString(R.string.save_path_name));
TextView label = (TextView)findViewById(R.id.save_points_label2);
label.setVisibility(View.INVISIBLE);
EditText step = (EditText)findViewById(R.id.save_points_edit_step);
step.setVisibility(View.INVISIBLE);
}
Button cmd_save = (Button)findViewById(R.id.save_points_cmd_save);
cmd_save.setOnClickListener(new OnClickListener(){
// @Override
public void onClick(View arg0) {
EditText edit_returvalue = (EditText)findViewById(
R.id.save_points_edit_returnvalue);
Bundle extras = new Bundle();
EditText step = (EditText)findViewById(
R.id.save_points_edit_step);
Integer stepValue;
try {
stepValue = Integer.valueOf(step.getText().toString());
}
catch (Exception e) {
stepValue = new Integer(20);
}
extras.putInteger("step", stepValue);
SavePoints.this.setResult(RESULT_OK,
edit_returvalue.getText().toString(),
extras);
SavePoints.this.finish();
}
});
Button cmd_cancel = (Button)findViewById(R.id.save_points_cmd_cancel);
cmd_cancel.setOnClickListener(new OnClickListener(){
// @Override
public void onClick(View arg0) {
SavePoints.this.finish();
}
});
}
}
|
"/src/your_package_structure/LoadPath.java":
| Java: |
package net.mobilefight.trackbuilder;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class LoadPath extends ListActivity {
public static final int RESULT_EMPTY = 2;
private List<String> directoryEntries = new ArrayList<String>();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
try {
browseTo(getFileStreamPath(""));
} catch (FileNotFoundException e) {
LoadPath.this.setResult(RESULT_EMPTY);
LoadPath.this.finish();
}
}
private void browseTo(final File aDirectory){
if (aDirectory.isDirectory()){
fill(aDirectory.listFiles());
}else{
OnClickListener okButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
LoadPath.this.setResult(RESULT_OK, aDirectory.getName());
LoadPath.this.finish();
}
};
OnClickListener cancelButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
// Do nothing ^^
}
};
AlertDialog.show(this,"Question", "Do you want to load: "
+ aDirectory.getName() + "?",
"OK", okButtonListener,
"Cancel", cancelButtonListener, false, null);
}
}
private void fill(File[] files) {
directoryEntries.clear();
boolean empty = true;
for (File file : files){
String filename = file.getName();
if (filename.endsWith("_path")) {
directoryEntries.add(filename);
empty = false;
}
}
ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
R.layout.file_row, this.directoryEntries);
this.setListAdapter(directoryList);
if (empty) {
LoadPath.this.setResult(RESULT_EMPTY);
LoadPath.this.finish();
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
int selectionRowID = (int) this.getSelectionRowID();
File clickedFile = null;
clickedFile = new File(this.directoryEntries.get(selectionRowID));
if(clickedFile != null)
this.browseTo(clickedFile);
}
}
|
Regards,
jeremian.
| Description: |
|
 Download |
| Filename: |
TrackBuilder.zip |
| Filesize: |
19.18 KB |
| Downloaded: |
825 Time(s) |
Last edited by jeremian on Sun Dec 30, 2007 10:24 pm; edited 1 time in total |
|
| Back to top |
|
 |
|
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
Posted: Fri Dec 28, 2007 2:53 pm Post subject: |
|
|
Hello jeremian,
this is so damn useful for developers of all map-applications
Big thx for letting us participate in your code.
Just one bad I got : A bit of commenting in the tricky parts would be fine
Regards,
plusminus
_________________
Download my apps  Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
jeremian Freshman

Joined: 13 Dec 2007 Posts: 6
|
Posted: Sun Dec 30, 2007 10:27 pm Post subject: |
|
|
Hi,
I have updated the tutorial. The list of changes:
- Added save/load of the paths.
- Commented the code.
- Fixed generation of the track.
Regards,
jeremian.
|
|
| Back to top |
|
 |
bavarol Experienced Developer

Joined: 10 Dec 2007 Posts: 52
|
Posted: Thu Jan 31, 2008 10:21 am Post subject: |
|
|
Hi, it's a wonderful program but I have a foolish question, how can I see that Menu?
I cannot access it.
Kind Regards
Edit: I know it, thank you anyway
|
|
| Back to top |
|
 |
res Senior Developer

Joined: 20 Nov 2007 Posts: 147 Location: United States
|
Posted: Mon Feb 04, 2008 11:04 pm Post subject: |
|
|
Excellent tutorial jeremian.
bavarol: you need to the push the middle button below the emulator screen to bring up the menu. Its between the "home" and "back" keys on the emulator.
|
|
| Back to top |
|
 |
don Junior Developer

Joined: 07 Dec 2007 Posts: 18
|
Posted: Sun Feb 10, 2008 2:05 am Post subject: Can I change the LocationProvider dynamically? |
|
|
Thanks for the post Jeremian, it has proven helpful so far, though I did struggle figuring out the adb part (btw, for any other newbs like me out there the step-by-step for adb is Here) I was wondering however, is it possible to dynamically create or alter the LocationProvider GPS sentences in real time?
As an example of what mean, imagine the Traveling Salesperson problem. The users would select different cities on the map, the code would then build the graph and determine the shortest path, then create or alter the locationProvider to show the GPS overlay moving through the graph of cities (hopefully this example isn't confusing the issue!).
The TrackBuilder app is very close to what I am looking for, except for the manual copy with ADB. Any ideas on how to do that step programmatically?
Any help would be greatly appreciated.
|
|
| Back to top |
|
 |
|
|
 |
fernan.do Freshman

Joined: 16 Feb 2008 Posts: 2
|
Posted: Sat Feb 16, 2008 12:09 am Post subject: Simple correction for new SDK |
|
|
Hi Jeremian!
Awesome tutorial, for those that are as excited as I am about the new sdk here is a small correction to a deprecated method:
inside the thread that shows the message in TestTrack.java
| Java: | Looper.prepare();
if (resultOK) {
showAlert("File Saved", "Track Saved as "+data,
"OK",false);
} else {
showAlert("Error","Track could not be saved as "+data,
"OK",false);
} |
should be replaced with:
| Java: | Looper.prepare();
if (resultOK) {
showAlert("File Saved",0, "Track Saved as "+data,
"OK",false);
} else {
showAlert("Error",0,"Track could not be saved as "+data,
"OK",false);
} |
(the new parameter is simple a resource id of an icon to display which in this case is zero since we do not
display any)
simple correction, yet annoying to find! Hope I can save someone some time.
Best of Luck! And thanks again Jeremian.
|
|
| Back to top |
|
 |
fernan.do Freshman

Joined: 16 Feb 2008 Posts: 2
|
Posted: Sat Feb 16, 2008 5:33 am Post subject: Make that for every one... |
|
|
Extending the comment above,
All showAlert methods should be extended to be of the form:
public abstract DialogInterface showAlert(CharSequence title, int iconId, CharSequence message, CharSequence buttonText, boolean cancelable)
meaning the parameter int iconId should be added.
|
|
| Back to top |
|
 |
jeremian Freshman

Joined: 13 Dec 2007 Posts: 6
|
|
| Back to top |
|
 |
wmellon Freshman

Joined: 03 Mar 2008 Posts: 3 Location: Rochester, NY
|
Posted: Mon Mar 03, 2008 5:17 am Post subject: |
|
|
Jeremian, et al.
Thanks for posting your TrackBuilder updates for the new SDK. I was following along great until I tried to add the saved track to the list of GPS providers. My steps:
1. Add points, save track, do adb pull / push on 6,432 byte track file (attached).
2. adb echoes back 402 KB/s (0 bytes in 6432.000s). I'll go out on a limb here and say that the I/O operations worked fine, and that the adb guys just got these data backwards.
3. Select "GPS providers" from TrackBuilder menu, but here only "none" and "gps" are shown; no uploaded track file.
Is there any way to view the file contents of the device to make sure the track file got to /data/misc/location/ ? Am I missing an important step?
Thanks again -- Will
| Description: |
|
 Download |
| Filename: |
track.rar |
| Filesize: |
1.09 KB |
| Downloaded: |
267 Time(s) |
|
|
| Back to top |
|
 |
gvkreddyvamsi Developer

Joined: 21 Jan 2008 Posts: 43 Location: INDIA
|
Posted: Mon Mar 03, 2008 6:10 am Post subject: vamsi |
|
|
HI,
I have loaded with Track Builder application..
Working well without alert boxes.
As syntaxes changed in m5 ...
plz suggest what we have to initialize with context in Toast
Toast.makeText(Context , "You have to put at least two points on the map!", Toast.LENGTH_SHORT).show();
I got error at Context..
thanks in advance.
Any idea?
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
|
| Back to top |
|
 |
gvkreddyvamsi Developer

Joined: 21 Jan 2008 Posts: 43 Location: INDIA
|
Posted: Mon Mar 03, 2008 9:51 am Post subject: vamsi |
|
|
Hello plusminus,
I have used that Toast syntax to replace notification manager .
In TrackBuilder class notification manager is used in 2-3 methods.
I am replacing all those with Toast method.
Now these are working fine with Context this.
And one thing ...here showAlert method is used at a lot places.
It is not working in m5.
How we can replace this method.
showAlert("File loaded", "Path file " + data + " successfully loaded", "OK", false);
Thank u.
by
vamsi
|
|
| Back to top |
|
 |
wannaberunner Freshman

Joined: 03 Mar 2008 Posts: 3
|
Posted: Mon Mar 03, 2008 4:55 pm Post subject: |
|
|
Hi all,
first of all I'd like to say thx to the author since the tutorial is really great!
However, I'm running m5 sdk and after I'd upload created track file to the emulator, it wasn't being recognized as a Location Provider. Only after I copied the properties file from default Gps provider, my new provider was recognized.
It took me two hours to figure it so I hope I save time for the others.
|
|
| Back to top |
|
 |
wmellon Freshman

Joined: 03 Mar 2008 Posts: 3 Location: Rochester, NY
|
Posted: Mon Mar 03, 2008 7:35 pm Post subject: |
|
|
wannabe,
It sounds like you and I were experiencing the same problem. Could you be please be more specific about how you "copied the properties file from the default Gps provider?" I'm not sure what this means, or perhaps more importantly, why it worked. Thanks so much for the post. It's good to know I'm not alone in my frustration as I'm learning the ins and outs of this great new platform.
--Will
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
© 2007, Android Development Community
All rights reserved.
Powered by phpBB.
|