andbook!.pdf - Learning Android Get an anddev.org - Android-Shirt Back to index
anddev.org Header Logo
FAQ Search Top rated articles Browse Feeds anddev.org - Authors Contact Details Register Log in

TrackBuilder for mock location providers

Goto page 1, 2  Next
 
       anddev.org - Android Development Community | Android Tutorials | Index -> Map Tutorials
Author Message
jeremian
Freshman
Freshman


Joined: 13 Dec 2007
Posts: 6

PostPosted: Thu Dec 27, 2007 10:41 pm    Post subject: TrackBuilder for mock location providers Reply with quote

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.



TrackBuilder.zip
 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
View user's profile Send private message
plusminus
Site Admin
Site Admin


Joined: 14 Nov 2007
Posts: 2655
Location: College Park, MD

PostPosted: Fri Dec 28, 2007 2:53 pm    Post subject: Reply with quote

Hello jeremian, Smile

this is so damn useful for developers of all map-applications Exclamation
Big thx for letting us participate in your code.

Just one bad I got Wink : A bit of commenting in the tricky parts would be fine Smile

Regards,
plusminus

_________________
Download my apps Idea
Please remember, that this board is give & take Smile


| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
jeremian
Freshman
Freshman


Joined: 13 Dec 2007
Posts: 6

PostPosted: Sun Dec 30, 2007 10:27 pm    Post subject: Reply with quote

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
View user's profile Send private message
bavarol
Experienced Developer
Experienced Developer


Joined: 10 Dec 2007
Posts: 52

PostPosted: Thu Jan 31, 2008 10:21 am    Post subject: Reply with quote

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
View user's profile Send private message
res
Senior Developer
Senior Developer


Joined: 20 Nov 2007
Posts: 147
Location: United States

PostPosted: Mon Feb 04, 2008 11:04 pm    Post subject: Reply with quote

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
View user's profile Send private message
don
Junior Developer
Junior Developer


Joined: 07 Dec 2007
Posts: 18

PostPosted: Sun Feb 10, 2008 2:05 am    Post subject: Can I change the LocationProvider dynamically? Reply with quote

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
View user's profile Send private message
fernan.do
Freshman
Freshman


Joined: 16 Feb 2008
Posts: 2

PostPosted: Sat Feb 16, 2008 12:09 am    Post subject: Simple correction for new SDK Reply with quote

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
View user's profile Send private message
fernan.do
Freshman
Freshman


Joined: 16 Feb 2008
Posts: 2

PostPosted: Sat Feb 16, 2008 5:33 am    Post subject: Make that for every one... Reply with quote

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
View user's profile Send private message
jeremian
Freshman
Freshman


Joined: 13 Dec 2007
Posts: 6

PostPosted: Sun Feb 24, 2008 9:06 pm    Post subject: Reply with quote

Hi,

I have updated the TrackBuilder sources to use m5-rc14 SDK.

List of changes:

- the mentioned iconId arguments have been added to showAlert()
- removed menu.addSeparator() calls
- Bundle.getInteger() (putInteger) has been renamed to Bundle.getInt() (putInt)
- removed calling of getProviders() in additional thread - in the current Android this method returns so fast, that can be used synchronously
- used Toast.makeText(...).show() instead NotificationManager.notifyWithText(...)
- used getSelectedItemId() instead getSelectionRowID()
- small modifications in manifest/layout files:
* id --> android:id
* class --> android:name
* android:value --> android:name
* added two new permissions: ACCESS_GPS and ACCESS_LOCATION

I hope the next Android SDK will be more back-compatible Wink



TrackBuilder.zip
 Description:

Download
 Filename:  TrackBuilder.zip
 Filesize:  18.91 KB
 Downloaded:  514 Time(s)

Back to top
View user's profile Send private message
wmellon
Freshman
Freshman


Joined: 03 Mar 2008
Posts: 3
Location: Rochester, NY

PostPosted: Mon Mar 03, 2008 5:17 am    Post subject: Reply with quote

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



track.rar
 Description:
my saved track file

Download
 Filename:  track.rar
 Filesize:  1.09 KB
 Downloaded:  267 Time(s)

Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
gvkreddyvamsi
Developer
Developer


Joined: 21 Jan 2008
Posts: 43
Location: INDIA

PostPosted: Mon Mar 03, 2008 6:10 am    Post subject: vamsi Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
plusminus
Site Admin
Site Admin


Joined: 14 Nov 2007
Posts: 2655
Location: College Park, MD

PostPosted: Mon Mar 03, 2008 9:28 am    Post subject: Reply with quote

Hello vamsi,

where is that line located Question
Java:
Toast.makeText(Context , "You have to put at least two points on the map!", Toast.LENGTH_SHORT).show();


Depending on the CodeContext, you need to use "this" or "OuterActivity.this".

Regards,
plusmonus

_________________
Download my apps Idea
Please remember, that this board is give & take Smile


| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
gvkreddyvamsi
Developer
Developer


Joined: 21 Jan 2008
Posts: 43
Location: INDIA

PostPosted: Mon Mar 03, 2008 9:51 am    Post subject: vamsi Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
wannaberunner
Freshman
Freshman


Joined: 03 Mar 2008
Posts: 3

PostPosted: Mon Mar 03, 2008 4:55 pm    Post subject: Reply with quote

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. Smile
Back to top
View user's profile Send private message
wmellon
Freshman
Freshman


Joined: 03 Mar 2008
Posts: 3
Location: Rochester, NY

PostPosted: Mon Mar 03, 2008 7:35 pm    Post subject: Reply with quote

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
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Display posts from previous:   
       anddev.org - Android Development Community | Android Tutorials | Index -> Map Tutorials All times are GMT + 1 Hour
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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.