New compass look

Aripuca GPS Tracker presents new look of its compass. Enjoy compass with rotating bezel and built in bubble level.

Compass ativity implements onTouchListener interface

Here is a quick example how to rotate compass by touch event.


@Override
 public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
 downX = event.getX();
 downY = event.getY();
 return true;

case MotionEvent.ACTION_MOVE:

upX = event.getX();
 upY = event.getY();

double downR = Math.atan2(v.getHeight() / 2 - downY, downX - v.getWidth() / 2);
 int angle1 = (int) Math.toDegrees(downR);

double upR = Math.atan2(v.getHeight() / 2 - upY, upX - v.getWidth() / 2);
 int angle2 = (int) Math.toDegrees(upR);

this.rotateCompass(angle1 - angle2);

// update starting point for next move event
 downX = upX;
 downY = upY;

return true;

}

return false;

}
Posted in Android Development, Aripuca, Tutorials | Leave a comment

Scheduled track recording

This feature makes Aripuca Tracker unique among other GPS tracking apps. The idea is dead simple: points got recorded based on predefined schedule not in real time. So GPS sensor is turned off between requests and not draining battery.

See help for details

Posted in Aripuca | Leave a comment

Available in Android Market!

Aripuca Tracker now available in Android Market!


Available in Android Market

Posted in Aripuca | Leave a comment

Restore sqlite database from external file

First step in restoring database is selecting the actual file.


	private void restoreDatabase() {

		// show "select a file" dialog
		File importFolder = new File(myApp.getAppDir() + "/backup/");
		final String importFiles[] = importFolder.list();

		if (importFiles == null ||
				importFiles.length == 0) {
			Toast.makeText(MainActivity.this, R.string.source_folder_empty, Toast.LENGTH_SHORT).show();
			return;
		}

		importDatabaseFileName = importFiles[0];

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(R.string.select_file);
		builder.setSingleChoiceItems(importFiles, 0, new
				DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {

						importDatabaseFileName = importFiles[whichButton];
						mainHandler.post(restoreDatabaseRunnable);

						dialog.dismiss();

					}
				});

		AlertDialog alert = builder.create();
		alert.show();

	}

Since we don’t know how much time the restoration process will take we do this in Runnable.


private Runnable restoreDatabaseRunnable = new Runnable() {
		@Override
		public void run() {

			try {

				// open database in readonly mode
				SQLiteDatabase db = SQLiteDatabase.openDatabase(
						myApp.getAppDir() + "/backup/" + importDatabaseFileName,
						null, SQLiteDatabase.OPEN_READONLY);

				// check version compatibility
				// only same version of the db can be restored
				if (myApp.getDatabase().getVersion() != db.getVersion()) {
					Toast.makeText(MainActivity.this, getString(R.string.restore_db_version_conflict),
							Toast.LENGTH_LONG).show();
					return;
				}

				db.close();

			} catch (SQLiteException e) {

				Toast.makeText(MainActivity.this, getString(R.string.restore_file_error) + ": " + e.getMessage(),
						Toast.LENGTH_LONG).show();

				return;
			}

			// closing current db
			myApp.getDatabase().close();

			try {

				File data = Environment.getDataDirectory();

				if (myApp.getExternalStorageWriteable()) {

					String restoreDBPath = myApp.getAppDir() + "/backup/" + importDatabaseFileName;

					File restoreDB = new File(restoreDBPath);
					File currentDB = new File(data, "/data/com.aripuca.tracker/databases/AripucaTracker.db");

					FileChannel src = new FileInputStream(restoreDB).getChannel();
					FileChannel dst = new FileOutputStream(currentDB).getChannel();

					dst.transferFrom(src, 0, src.size());
					src.close();
					dst.close();

					myApp.setDatabase();

				}

			}
			catch (Exception e) {

				Log.e(Constants.TAG, e.getMessage());

				myApp.setDatabase();

			}

		}
	};
Posted in Android Development, Tutorials | Leave a comment

Backup database to external storage

Here is an example how to backup application database to external storage:

			File data = Environment.getDataDirectory();

			if (myApp.getExternalStorageWriteable()) {

				String currentDBPath = "/data/com.aripuca.tracker/databases/" + Constants.APP_NAME + ".db";

				String dateStr = (new SimpleDateFormat("yyyyMMdd_HHmmss")).format(new Date());

				File currentDB = new File(data, currentDBPath);
				File backupDB = new File(myApp.getAppDir() + "/backup/" + dateStr + ".db");

				if (currentDB.exists()) {
					FileChannel src = new FileInputStream(currentDB).getChannel();
					FileChannel dst = new FileOutputStream(backupDB).getChannel();
					dst.transferFrom(src, 0, src.size());
					src.close();
					dst.close();
				} else {
				}

			}

In next post I will publish working example of how to restore database from external storage and replace internal application db.

Posted in Android Development, Tutorials | Leave a comment

Simple compass

Implemented dedicated compass activity. Can display True and Magnetic North simultaneously (Magnetic North is semi-transparent).

The activity registers broadcast receiver and listens to updates from GPS service.

Compass Activity onResume method

public void onResume() {

	// registering receiver for compass updates
	IntentFilter filter = new IntentFilter("com.aripuca.tracker.COMPASS_UPDATES_ACTION");
	registerReceiver(compassBroadcastReceiver, filter);

	super.onResume();

}

Broadcast receiver declaration:

protected BroadcastReceiver compassBroadcastReceiver = new BroadcastReceiver() {
	@Override
	public void onReceive(Context context, Intent intent) {
		Bundle bundle = intent.getExtras();
		updateCompass(bundle.getFloat("azimuth"));
	}
};

Sending broadcast intent from GpsService


public void onSensorChanged(SensorEvent event) {

	// let's broadcast compass data to any activity waiting for updates
	Intent intent = new Intent("com.aripuca.tracker.COMPASS_UPDATES_ACTION");

	// packing azimuth value into bundle
	Bundle bundle = new Bundle();
	bundle.putFloat("azimuth", event.values[0]);

	intent.putExtras(bundle);			

	// broadcasting compass updates
	sendBroadcast(intent);

}

For complete working example see project source code.

Posted in Android Development, Tutorials | Leave a comment

Segmenting by time interval

Another new featured has been added to Aripuca Tracker. Segmenting by time interval is really useful when you want to know what distances you can cover in fixed time.

Segments are now displayed on a map with alternating colors

Posted in Aripuca | Leave a comment

Track segmenting

Just finished working on track segmenting. Implemented 3 segmenting modes:

Manual
New segment will be created when you click pause button any time during track recording

Every N km
New segment will be created automatically every N km. Configured in settings.

Custom intervals
New segment is created automatically based on predefined set of distances where to cut the track. Ex: 5,7,9,12,17,22. So the lengths of segments will be: 5, 2, 2, 3, 5. If trip is longer than 22 km – the remainder goes to the last segment.

I found it really useful when you use the same route over and over again. Dividing the track by known segments based on unique trip features, like hills, traffic lights, different types of roads, etc, makes comparing tracks by segments much more detailed.

All track statistics (distance, min/max speed, trip time, min/max elevation, elevation gain/loss, etc) will be available for analizing right after track recording stopped.

Posted in Aripuca | Leave a comment

Google code project started

Aripuca Tracker is now officially open-source project hosted at Google Code. So happy that Google supports Mercurial!

http://code.google.com/p/aripuca-tracker/

Posted in Aripuca | Leave a comment

Formatting time interval

Here is a simple function converting time interval in milliseconds to 0:00:00 format. It’s a good example of using StringBuilder class too.

public static String formatInterval(long milliseconds, boolean showHours) {

	int seconds = Math.round(milliseconds/1000.0f);

	int hours = (int) (seconds / 3600);
	int minutes = (int) (seconds / 60);
	if (minutes >= 60) {
		minutes = (int) (minutes % 60);
	}
	seconds = (int) (seconds % 60);

	StringBuilder builder = new StringBuilder();

	if (hours > 0 || showHours) {
		builder.append(hours);
		builder.append(":");
	}

	if (minutes <= 9) {
		builder.append("0");
	}
	builder.append(minutes);

	builder.append(":");

	if (seconds <= 9) {
		builder.append("0");
	}
	builder.append(seconds);

	return builder.toString();

}
Posted in Android Development, Tutorials | Leave a comment