Commits

littledot5566  committed e6899b8

Changed prefix Hunting* to Expedition*

  • Participants
  • Parent commits f2cb6bb

Comments (0)

Files changed (86)

File AndroidManifest.xml

 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application
         </activity>
         <!--  -->
         <activity
-            android:name=".HuntingActivity"
+            android:name=".ExpeditionActivity"
             android:configChanges="keyboardHidden|orientation"
             android:screenOrientation="portrait" >
         </activity>

File bin/AndroidManifest.xml

+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="nctuw.littledot.localtreasure"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="10"
+        android:targetSdkVersion="15" />
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+
+        <!--  -->
+        <uses-library android:name="com.google.android.maps" />
+
+        <activity
+            android:name=".SplashActivity"
+            android:label="@string/title_activity_main" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <!--  -->
+        <activity
+            android:name=".ExpeditionActivity"
+            android:configChanges="keyboardHidden|orientation"
+            android:screenOrientation="portrait" >
+        </activity>
+        <!--  -->
+        <activity
+            android:name=".HuntingMapActivity"
+            android:configChanges="keyboardHidden|orientation"
+            android:screenOrientation="portrait" >
+        </activity>
+        <!--  -->
+        <activity android:name=".MainActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".SelectDistanceActivity" >
+        </activity>
+    </application>
+
+</manifest>

File bin/LocalTreasure.apk

Binary file added.

File bin/classes.dex

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/BuildConfig.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/DataManager.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/Database$DatabaseHelper.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/Database.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/DialogManager.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/ExpeditionActivity$1.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/ExpeditionActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/ExpeditionCompleteActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/ExpeditionListener.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/ExpeditionManager.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/Geodesy.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/HuntingMapActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/MainActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/MapOverlay.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$attr.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$dimen.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$drawable.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$id.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$layout.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$menu.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$string.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R$style.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/R.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/SelectDistanceActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/SplashActivity$1.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/SplashActivity.class

Binary file added.

File bin/classes/nctuw/littledot/localtreasure/Treasure.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$attr.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$dimen.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$drawable.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$id.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$layout.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$menu.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$string.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R$style.class

Binary file added.

File bin/classes/nctuwaterloo/littledot/utils/R.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/Angle.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/Ellipsoid.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/Example.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/GeodeticCalculator.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/GeodeticCurve.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/GeodeticMeasurement.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/GlobalCoordinates.class

Binary file added.

File bin/classes/org/gavaghan/geodesy/GlobalPosition.class

Binary file added.

File bin/jarlist.cache

+# cache for current jar dependecy. DO NOT EDIT.
+# format is <lastModified> <length> <SHA-1> <path>
+# Encoding is UTF-8
+1341783862000 337562 27c24d26e4c5d57976e6926367985548678e913c /home/littledot/Code/Android/LocalTreasure/libs/android-support-v4.jar
+1342574347000 337562 27c24d26e4c5d57976e6926367985548678e913c /home/littledot/Code/Android/Utils/libs/android-support-v4.jar

File bin/res/drawable-hdpi/ic_action_search.png

Added
New image

File bin/res/drawable-hdpi/ic_launcher.png

Added
New image

File bin/res/drawable-ldpi/ic_launcher.png

Added
New image

File bin/res/drawable-mdpi/ic_action_search.png

Added
New image

File bin/res/drawable-mdpi/ic_launcher.png

Added
New image

File bin/res/drawable-xhdpi/ic_action_search.png

Added
New image

File bin/res/drawable-xhdpi/ic_launcher.png

Added
New image

File bin/res/drawable/androidmarker.png

Added
New image

File bin/res/drawable/blue_map_marker_32_32.png

Added
New image

File bin/res/drawable/green_map_marker_32_32.png

Added
New image

File bin/res/drawable/green_map_marker_64_64.png

Added
New image

File bin/res/drawable/pink_map_marker_32_32.png

Added
New image

File bin/res/drawable/treasure_apple_256.png

Added
New image

File bin/res/drawable/treasure_banana_256.png

Added
New image

File bin/res/drawable/treasure_orange_256.png

Added
New image

File bin/resources.ap_

Binary file added.

File gen/nctuw/littledot/localtreasure/BuildConfig.java

+/** Automatically generated file. DO NOT MODIFY */
+package nctuw.littledot.localtreasure;
+
+public final class BuildConfig {
+    public final static boolean DEBUG = true;
+}

File gen/nctuw/littledot/localtreasure/R.java

+/* AUTO-GENERATED FILE.  DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found.  It
+ * should not be modified by hand.
+ */
+
+package nctuw.littledot.localtreasure;
+
+public final class R {
+    public static final class attr {
+    }
+    public static final class dimen {
+        public static final int padding_large=0x7f060002;
+        public static final int padding_medium=0x7f060001;
+        public static final int padding_small=0x7f060000;
+    }
+    public static final class drawable {
+        public static final int androidmarker=0x7f020000;
+        public static final int blue_map_marker_32_32=0x7f020001;
+        public static final int green_map_marker_32_32=0x7f020002;
+        public static final int green_map_marker_64_64=0x7f020003;
+        public static final int ic_action_search=0x7f020004;
+        public static final int ic_launcher=0x7f020005;
+        public static final int pink_map_marker_32_32=0x7f020006;
+        public static final int treasure_apple_256=0x7f020007;
+        public static final int treasure_banana_256=0x7f020008;
+        public static final int treasure_orange_256=0x7f020009;
+    }
+    public static final class id {
+        public static final int distance_but_500m=0x7f08000e;
+        public static final int distance_but_custom=0x7f080011;
+        public static final int distance_et_custom=0x7f080010;
+        public static final int distance_ll_custom=0x7f08000f;
+        public static final int distance_tv_title=0x7f08000d;
+        public static final int geolock_ll_content=0x7f080001;
+        public static final int geolock_pb_circle=0x7f080002;
+        public static final int geolock_tv_content=0x7f080003;
+        public static final int geolock_tv_title=0x7f080000;
+        public static final int hunt_but_showmap=0x7f080008;
+        public static final int hunt_tv_bearing=0x7f080007;
+        public static final int hunt_tv_cur_location=0x7f080004;
+        public static final int hunt_tv_dest_location=0x7f080005;
+        public static final int hunt_tv_distance=0x7f080006;
+        public static final int main_but_startHunt=0x7f08000a;
+        public static final int main_tv_info=0x7f080009;
+        public static final int map_but_dig=0x7f08000c;
+        public static final int mapview=0x7f08000b;
+        public static final int menu_settings=0x7f080013;
+        public static final int splash_tv_appName=0x7f080012;
+    }
+    public static final class layout {
+        public static final int geolock_dia_lay=0x7f030000;
+        public static final int hunting_act_layout=0x7f030001;
+        public static final int main_act_layout=0x7f030002;
+        public static final int map_act_layout=0x7f030003;
+        public static final int selectdistance_act_layout=0x7f030004;
+        public static final int splash_act_layout=0x7f030005;
+    }
+    public static final class menu {
+        public static final int activity_main=0x7f070000;
+    }
+    public static final class string {
+        public static final int app_name=0x7f040000;
+        public static final int distance_but_500m=0x7f040006;
+        public static final int distance_but_custom=0x7f040008;
+        public static final int distance_et_custom=0x7f040007;
+        public static final int distance_tv_title=0x7f040005;
+        public static final int geolock_tv_title=0x7f040003;
+        public static final int main_but_startHunt=0x7f040004;
+        public static final int menu_settings=0x7f040001;
+        public static final int title_activity_main=0x7f040002;
+    }
+    public static final class style {
+        public static final int AppTheme=0x7f050000;
+    }
+}

File gen/nctuwaterloo/littledot/utils/R.java

+/* AUTO-GENERATED FILE.  DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found.  It
+ * should not be modified by hand.
+ */
+
+package nctuwaterloo.littledot.utils;
+
+public final class R {
+    public static final class attr {
+    }
+    public static final class dimen {
+        public static final int padding_large=0x7f060002;
+        public static final int padding_medium=0x7f060001;
+        public static final int padding_small=0x7f060000;
+    }
+    public static final class drawable {
+        public static final int androidmarker=0x7f020000;
+        public static final int blue_map_marker_32_32=0x7f020001;
+        public static final int green_map_marker_32_32=0x7f020002;
+        public static final int green_map_marker_64_64=0x7f020003;
+        public static final int ic_action_search=0x7f020004;
+        public static final int ic_launcher=0x7f020005;
+        public static final int pink_map_marker_32_32=0x7f020006;
+        public static final int treasure_apple_256=0x7f020007;
+        public static final int treasure_banana_256=0x7f020008;
+        public static final int treasure_orange_256=0x7f020009;
+    }
+    public static final class id {
+        public static final int distance_but_500m=0x7f08000e;
+        public static final int distance_but_custom=0x7f080011;
+        public static final int distance_et_custom=0x7f080010;
+        public static final int distance_ll_custom=0x7f08000f;
+        public static final int distance_tv_title=0x7f08000d;
+        public static final int geolock_ll_content=0x7f080001;
+        public static final int geolock_pb_circle=0x7f080002;
+        public static final int geolock_tv_content=0x7f080003;
+        public static final int geolock_tv_title=0x7f080000;
+        public static final int hunt_but_showmap=0x7f080008;
+        public static final int hunt_tv_bearing=0x7f080007;
+        public static final int hunt_tv_cur_location=0x7f080004;
+        public static final int hunt_tv_dest_location=0x7f080005;
+        public static final int hunt_tv_distance=0x7f080006;
+        public static final int main_but_startHunt=0x7f08000a;
+        public static final int main_tv_info=0x7f080009;
+        public static final int map_but_dig=0x7f08000c;
+        public static final int mapview=0x7f08000b;
+        public static final int menu_settings=0x7f080013;
+        public static final int splash_tv_appName=0x7f080012;
+    }
+    public static final class layout {
+        public static final int geolock_dia_lay=0x7f030000;
+        public static final int hunting_act_layout=0x7f030001;
+        public static final int main_act_layout=0x7f030002;
+        public static final int map_act_layout=0x7f030003;
+        public static final int selectdistance_act_layout=0x7f030004;
+        public static final int splash_act_layout=0x7f030005;
+    }
+    public static final class menu {
+        public static final int activity_main=0x7f070000;
+    }
+    public static final class string {
+        public static final int app_name=0x7f040000;
+        public static final int distance_but_500m=0x7f040006;
+        public static final int distance_but_custom=0x7f040008;
+        public static final int distance_et_custom=0x7f040007;
+        public static final int distance_tv_title=0x7f040005;
+        public static final int geolock_tv_title=0x7f040003;
+        public static final int main_but_startHunt=0x7f040004;
+        public static final int menu_settings=0x7f040001;
+        public static final int title_activity_main=0x7f040002;
+    }
+    public static final class style {
+        public static final int AppTheme=0x7f050000;
+    }
+}

File release/LocalTreasure.apk

Binary file added.

File res/drawable/blue_map_marker_32_32.png

Added
New image

File res/drawable/green_map_marker_32_32.png

Added
New image

File res/drawable/green_map_marker_64_64.png

Added
New image

File res/drawable/pink_map_marker_32_32.png

Added
New image

File res/drawable/treasure_apple_256.png

Added
New image

File res/drawable/treasure_banana_256.png

Added
New image

File res/drawable/treasure_orange_256.png

Added
New image

File res/layout/map_act_layout.xml

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
     android:orientation="vertical" >
 
     <com.google.android.maps.MapView
         android:id="@+id/mapview"
         android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
         android:apiKey="0-0tVBYzo78GcruEETjNXCnXjt4rzTKIkzU5HAQ"
         android:clickable="true" />
 
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/map_but_dig"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="onClick"
+            android:text="Start Digging!" />
+    </LinearLayout>
+
 </LinearLayout>

File src/nctuw/littledot/localtreasure/DataManager.java

 import android.content.SharedPreferences;
 
 public class DataManager {
-	private Context						mCtx;
-	private SharedPreferences	mPrefs;
+	public static final String				DBTABLE_STATS				= "statistics";
+	public static final String				KEY_STATS_ID				= "sID";
+	public static final String				KEY_STATS_DIST_TRAV	= "sDistanceTraveled";
+	public static final String				KEY_STATS_EXP_TOTAL	= "sTotalExpeditionsLaunched";
+	public static final String				KEY_STATS_EXP_SUCC	= "sSuccessfulExpeditions";
+	public static final String				KEY_STATS_EXP_FAIL	= "sFailedExpeditions";
+
+	private Context										mCtx;
+	private SharedPreferences					mPrefs;
+	private SharedPreferences.Editor	mEditor;
 
 	public DataManager(Context ctx) {
 		mCtx = ctx;
 
-		mPrefs = mCtx.getSharedPreferences("treasure", 0);
+		mPrefs = mCtx.getSharedPreferences("nctuw.littledot.localtreasure",
+				Context.MODE_PRIVATE);
+
+		SharedPreferences.Editor editor = mPrefs.edit();
+		// editor.putLong(Constants.PREF_LAST_AD_CLICK, cal.getTimeInMillis());
+		// editor.commit();
+
+	}
+
+	public float getDistanceTraveled() {
+		return mPrefs.getFloat(KEY_STATS_DIST_TRAV, 0);
+	}
+
+	public void incDistanceTraveled(float inc) {
+		float value = getDistanceTraveled() + inc;
+		mEditor.putFloat(KEY_STATS_DIST_TRAV, value);
+		mEditor.commit();
+	}
+
+	public int getTotalExpeditions() {
+		return mPrefs.getInt(KEY_STATS_EXP_TOTAL, 0);
+	}
+
+	public void incTotalExpeditions(int inc) {
+		int value = getTotalExpeditions() + inc;
+		mEditor.putInt(KEY_STATS_EXP_TOTAL, value);
+		mEditor.commit();
+	}
+
+	public int getSuccessExpeditions() {
+		return mPrefs.getInt(KEY_STATS_EXP_SUCC, 0);
+	}
+
+	public void incSuccessExpeditions(int inc) {
+		int value = getSuccessExpeditions() + inc;
+		mEditor.putInt(KEY_STATS_EXP_SUCC, value);
+		mEditor.commit();
+	}
+
+	public int getFailedExpeditions() {
+		return mPrefs.getInt(KEY_STATS_EXP_FAIL, 0);
+	}
 
+	public void incFailedExpeditions(int inc) {
+		int value = getFailedExpeditions();
+		mEditor.putInt(KEY_STATS_EXP_FAIL, value);
+		mEditor.commit();
 	}
 }

File src/nctuw/littledot/localtreasure/Database.java

+package nctuw.littledot.localtreasure;
+
+import nctuw.littledot.util.Leg;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class Database {
+	private static final int		DB_VERSION										= 1;
+	public static final String	DBNAME												= "nctuw.littledot.localtreasure";
+
+	public static final String	DBTABLE_TREASURE							= "treasure";
+	public static final String	KEY_TREASURE_ID								= "tID";
+	public static final String	KEY_TREASURE_NAME							= "tName";
+	public static final String	KEY_TREASURE_NUM							= "tDays";
+
+	public static final String	DBTABLE_PROFILE								= "profile";
+	public static final String	KEY_PROFILE_ID								= "pID";
+	public static final String	KEY_PROFILE_EXPEDITIONS				= "pExpeditions";
+	public static final String	KEY_PROFILE_DISTANCE_TRAVELED	= "pDistanceTraveled";
+
+	private static final String	SQL_CREATE_TREASURE						= "create table if not exists "
+																																+ DBTABLE_TREASURE
+																																+ "("
+																																+ KEY_TREASURE_ID
+																																+ " integer not null primary key,"
+																																+ KEY_TREASURE_NAME
+																																+ " text default '',"
+																																+ KEY_TREASURE_NUM
+																																+ " integer not null default 0"
+																																+ ");";
+
+	private static final String	SQL_CREATE_PROFILE						= "create table if not exists "
+																																+ DBTABLE_PROFILE
+																																+ "("
+																																+ KEY_PROFILE_ID
+																																+ " integer not null primary key autoincrement,"
+																																+ KEY_PROFILE_EXPEDITIONS
+																																+ " integer not null default 0,"
+																																+ KEY_PROFILE_DISTANCE_TRAVELED
+																																+ " real not null default 0"
+																																+ ");";
+
+	private final Context				mContext;
+
+	private DatabaseHelper			DBHelper;
+	private SQLiteDatabase			db;
+
+	public Database(Context ctx) {
+		this.mContext = ctx;
+		DBHelper = new DatabaseHelper(mContext);
+
+	}
+
+	private class DatabaseHelper extends SQLiteOpenHelper {
+		DatabaseHelper(Context context) {
+			super(context, DBNAME, null, DB_VERSION);
+		}
+
+		@Override
+		public void onCreate(SQLiteDatabase db) {
+			db.execSQL(SQL_CREATE_TREASURE);
+			db.execSQL(SQL_CREATE_PROFILE);
+		}
+
+		@Override
+		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+			db.execSQL("drop table if exists " + DBTABLE_TREASURE + ";");
+			db.execSQL("drop table if exists " + DBTABLE_PROFILE + ";");
+			onCreate(db);
+		}
+	}
+
+	public void open() {
+		db = DBHelper.getWritableDatabase();
+	}
+
+	public void close() {
+		if (db.isOpen())
+			db.close();
+	}
+
+	public int getVersion() {
+		return db.getVersion();
+	}
+
+	public void showAllData(String table) {
+		Cursor cur = db.query(table, null, null, null, null, null, null);
+		if (cur != null)
+			cur.moveToFirst();
+
+		while (!cur.isAfterLast()) {
+			String log = "";
+			for (int i = 4; i < 9; i++) {
+				log += "[" + i + "]=";
+				log += cur.getInt(i);
+			}
+			Leg.i(log);
+			cur.moveToNext();
+		}
+	}
+
+	/*************/
+	/** Queries **/
+	/*************/
+
+	public Cursor rawQuery(String sql) {
+		return rawQuery(sql, null);
+	}
+
+	public Cursor rawQuery(String sql, String[] args) {
+		Cursor cur = db.rawQuery(sql, args);
+
+		cur.moveToFirst();
+		return cur;
+	}
+
+	public int queryTreasureNum(Treasure treasure) {
+		Cursor cur = rawQuery(
+				"select * from ? where ? = ? ",
+				new String[] { DBTABLE_TREASURE, KEY_TREASURE_ID,
+						Integer.toString(treasure.getDrawableId()) });
+
+		if (cur.moveToFirst())
+			return cur.getInt(cur.getColumnIndexOrThrow(KEY_TREASURE_NUM));
+		else
+			return -1;
+	}
+
+	public int queryExpeditionNum(int profileID) {
+		Cursor cur = rawQuery("select * from ? where ? = ? ", new String[] {
+				DBTABLE_PROFILE, KEY_PROFILE_ID, Integer.toString(profileID) });
+
+		if (cur.moveToFirst())
+			return cur.getInt(cur.getColumnIndexOrThrow(KEY_PROFILE_EXPEDITIONS));
+		else
+			return -1;
+	}
+
+	public double queryDistanceTraveled(int profileID) {
+		Cursor cur = rawQuery("select * from ? where ? = ? ", new String[] {
+				DBTABLE_PROFILE, KEY_PROFILE_ID, Integer.toString(profileID) });
+
+		if (cur.moveToFirst())
+			return cur.getDouble(cur
+					.getColumnIndexOrThrow(KEY_PROFILE_DISTANCE_TRAVELED));
+		else
+			return -1;
+	}
+
+	/*************/
+	/** Inserts **/
+	/*************/
+
+	public long insert(String table, String[] keys, Object[] values) {
+		if (keys.length != values.length)
+			throw new IllegalArgumentException(
+					"keys-values must be of the same length");
+
+		ContentValues kvPair = new ContentValues();
+
+		for (int i = 0; i < keys.length; i++) {
+			if (values[i] instanceof String)
+				kvPair.put(keys[i], (String) values[i]);
+			else if (values[i] instanceof Integer)
+				kvPair.put(keys[i], (Integer) values[i]);
+			else if (values[i] instanceof Double)
+				kvPair.put(keys[i], (Double) values[i]);
+			else if (values[i] instanceof Float)
+				kvPair.put(keys[i], (Float) values[i]);
+			else if (values[i] instanceof Long)
+				kvPair.put(keys[i], (Long) values[i]);
+			else if (values[i] instanceof Short)
+				kvPair.put(keys[i], (Short) values[i]);
+			else if (values[i] instanceof Boolean)
+				kvPair.put(keys[i], (Boolean) values[i]);
+			else if (values[i] instanceof Byte)
+				kvPair.put(keys[i], (Byte) values[i]);
+			else if (values[i] instanceof byte[])
+				kvPair.put(keys[i], (byte[]) values[i]);
+			else
+				throw new IllegalArgumentException("Type "
+						+ values[i].getClass().getName() + " is not supported.");
+		}
+
+		long result = db.insert(table, null, kvPair);
+
+		return result;
+	}
+
+	// public long insertEvent(Event event) {
+	// ContentValues kvPair = new ContentValues();
+	//
+	// db = DBHelper.getWritableDatabase();
+	// long result = db.insert(DBTABLE_EVENT, null, kvPair);
+	// db.close();
+	// Leg.i("insertEvent=" + result);
+	//
+	// // set the alarm TODO:move this elsewhere
+	// if (event.getRingTime() > 0) {
+	// event.setEvtId((int) result);
+	// AlarmHandler ah = new AlarmHandler(mContext);
+	// ah.setWeeklyAlarm(event);
+	// }
+	//
+	// return result;
+	// }
+
+	/*************/
+	/** Updates **/
+	/*************/
+
+	public boolean update(String table, String[] keys, Object[] values,
+			String whereClause) {
+		if (keys.length != values.length)
+			throw new IllegalArgumentException(
+					"keys-values must be of the same length");
+
+		ContentValues kvPair = new ContentValues();
+		for (int i = 0; i < keys.length; i++) {
+			if (values[i] instanceof String)
+				kvPair.put(keys[i], (String) values[i]);
+			else if (values[i] instanceof Integer)
+				kvPair.put(keys[i], (Integer) values[i]);
+			else if (values[i] instanceof Double)
+				kvPair.put(keys[i], (Double) values[i]);
+			else if (values[i] instanceof Float)
+				kvPair.put(keys[i], (Float) values[i]);
+			else if (values[i] instanceof Long)
+				kvPair.put(keys[i], (Long) values[i]);
+			else if (values[i] instanceof Short)
+				kvPair.put(keys[i], (Short) values[i]);
+			else if (values[i] instanceof Boolean)
+				kvPair.put(keys[i], (Boolean) values[i]);
+			else if (values[i] instanceof Byte)
+				kvPair.put(keys[i], (Byte) values[i]);
+			else if (values[i] instanceof byte[])
+				kvPair.put(keys[i], (byte[]) values[i]);
+			else
+				throw new IllegalArgumentException("Type "
+						+ values[i].getClass().getName() + " is not supported.");
+		}
+
+		boolean result = db.update(table, kvPair, whereClause, null) > 0;
+
+		return result;
+	}
+
+	public boolean update(String table, String key, Object value,
+			String whereClause) {
+		return update(table, new String[] { key }, new Object[] { value },
+				whereClause);
+	}
+
+	/**
+	 * Increment the number of accumulated treasure by the specified amount.
+	 * 
+	 * @param treasure
+	 * @param increment
+	 * @return
+	 */
+	public void incrementTreasure(Treasure treasure, int increment) {
+		int ret = queryTreasureNum(treasure);
+
+		if (ret == -1) {
+			// do an insert if key does not exist yet
+			insert(DBTABLE_TREASURE, new String[] { KEY_TREASURE_NAME,
+					KEY_TREASURE_NUM }, new Object[] { treasure, increment });
+		} else {
+			// do an update if key exists
+			int newNum = ret + increment;
+
+			update(DBTABLE_TREASURE, KEY_TREASURE_NUM, newNum, KEY_TREASURE_NAME
+					+ " = " + treasure);
+		}
+	}
+
+	public void incrementExpeditions(int profileID, int increment) {
+		int ret = queryExpeditionNum(profileID);
+
+		if (ret == -1) {
+			insert(DBTABLE_PROFILE, new String[] { KEY_PROFILE_EXPEDITIONS,
+					KEY_PROFILE_DISTANCE_TRAVELED }, new Object[] { 0, 0 });
+		} else {
+			int newNum = ret + increment;
+
+			update(DBTABLE_PROFILE, KEY_PROFILE_EXPEDITIONS, newNum, KEY_PROFILE_ID
+					+ "=" + profileID);
+		}
+	}
+
+	public void incrementDistance(int profileID, double amount) {
+		double ret = queryDistanceTraveled(profileID);
+
+		if (ret == -1) {
+			insert(DBTABLE_PROFILE, new String[] { KEY_PROFILE_EXPEDITIONS,
+					KEY_PROFILE_DISTANCE_TRAVELED }, new Object[] { 0, 0 });
+		} else {
+			double newNum = ret + amount;
+
+			update(DBTABLE_PROFILE, KEY_PROFILE_DISTANCE_TRAVELED, newNum,
+					KEY_PROFILE_ID + "=" + profileID);
+		}
+	}
+
+	/*************/
+	/** Deletes **/
+	/*************/
+
+	public boolean delete(String table, String whereClaus, String[] whereArgs) {
+		boolean result = db.delete(table, whereClaus, whereArgs) > 0;
+
+		return result;
+	}
+
+	public boolean delete(String table, String whereClaus) {
+		return delete(table, whereClaus, null);
+	}
+
+	/*
+	 * public Cursor getEvent(long id) throws SQLException { Cursor cur =
+	 * db.query(true, DBTABLE_EVENT, null, KEY_EVENT_EID + "=" + id, null, null,
+	 * null, null, null); if (cur != null) cur.moveToFirst();
+	 * 
+	 * cursorReader(cur); return cur; }
+	 * 
+	 * public Cursor getEventsForTable(int tid, String[] neededColumns) { Cursor
+	 * mCursor = db.query(true, DBTABLE_EVENT, neededColumns, KEY_TABLE_TID + "="
+	 * + tid, null, null, null, null, null); if (mCursor != null)
+	 * mCursor.moveToFirst(); return mCursor; }
+	 * 
+	 * public Cursor query(String[] columns, String selection, String[]
+	 * selectionArgs) { Cursor mCursor = db.query(true, DBTABLE_EVENT, columns,
+	 * selection, selectionArgs, null, null, null, null); if (mCursor != null)
+	 * mCursor.moveToFirst(); return mCursor; }
+	 * 
+	 * private void cursorReader(Cursor cur) { while (!cur.isAfterLast()) { String
+	 * log = ""; for (int i = 0; i < cur.getColumnCount(); i++) log += "[" +
+	 * cur.getColumnName(i) + "]=" + cur.getString(i); //Leg.i( log);
+	 * cur.moveToNext(); } cur.moveToFirst(); }
+	 */
+
+	/***************/
+	/** Utilities **/
+	/***************/
+
+	public static String intArrToString(int[] intArray) {
+		StringBuilder retStr = new StringBuilder();
+		for (int i : intArray)
+			retStr.append(i);
+		return retStr.toString();
+	}
+
+	public static int[] stringToIntArr(String str) {
+		int len = str.length();
+		int[] retArr = new int[len];
+
+		for (int i = 0; i < len; i++)
+			retArr[i] = Integer.parseInt("" + str.charAt(i));
+		return retArr;
+	}
+}

File src/nctuw/littledot/localtreasure/ExpeditionActivity.java

+package nctuw.littledot.localtreasure;
+
+import nctuw.littledot.util.Echo;
+import nctuw.littledot.util.Leg;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.text.format.Time;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.widget.TextView;
+
+import com.google.android.maps.MapActivity;
+import com.google.android.maps.MapView;
+
+public class ExpeditionActivity extends MapActivity implements
+		LocationListener, SensorEventListener {
+	public static final long		LOCATION_TIME_THRESH	= 5 * 60 * 1000;
+	public static final float		LOCATION_ACC_THRESH		= 100.0f;
+	public static final int			LOCATION_LOCK_DIALOG	= 1;
+
+	public static final String	BUND_DIST							= "dist";
+	public static final String	BUND_BEAR							= "bear";
+	public static final String	BUND_STARTLOC					= "curr";
+	public static final String	BUND_DESTLOC					= "dest";
+	public static final String	BUND_DIST_TRAV				= "dist_trav";
+
+	private LayoutInflater			mLI;
+	private LocationManager			mLM;
+	private SensorManager				mSM;
+	private Database						mDB;
+	private DataManager					mDM;
+
+	private TextView						tvCurLoc;
+	private TextView						tvDestLoc;
+	private TextView						tvDistance;
+	private TextView						tvBearing;
+	private MapView							map;
+
+	private ProgressDialog			pdLock;
+
+	// game states
+	private boolean							isCalibrated					= false;
+	private boolean							atDest								= false;
+
+	private int									mDistance;
+	private double							mDistanceTraveled;
+	private double							mBearing;
+	private Location						mStartLocation;
+	private Location						mCurLocation;
+	private Location						mLKLocation;
+	private Location						mDestLocation;
+
+	private Location						lkGPS;
+	private Location						lkNetwork;
+	private Location						lkPassive;
+
+	private float								mAcceler[];
+	private float								mMagnetic[];
+	private MapOverlay					destMarker;														// destination
+	private MapOverlay					playerMarker;													// player
+	private MapOverlay					startMarker;														// start
+
+	private ExpeditionManager		ell;
+
+	/*
+	 * Base class Activity
+	 */
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.map_act_layout);
+
+		mLI = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+		mLM = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+		mSM = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+		mDB = new Database(this);
+		mDM = new DataManager(this);
+
+		// tvCurLoc = (TextView) findViewById(R.id.hunt_tv_cur_location);
+		// tvDestLoc = (TextView) findViewById(R.id.hunt_tv_dest_location);
+		// tvDistance = (TextView) findViewById(R.id.hunt_tv_distance);
+		// tvBearing = (TextView) findViewById(R.id.hunt_tv_bearing);
+		map = (MapView) findViewById(R.id.mapview);
+		pdLock = new ProgressDialog(this);
+
+		map.setBuiltInZoomControls(true);
+
+		map.getOverlays().add(
+				destMarker = new MapOverlay(getResources().getDrawable(
+						R.drawable.green_map_marker_32_32), this));
+		map.getOverlays().add(
+				playerMarker = new MapOverlay(getResources().getDrawable(
+						R.drawable.pink_map_marker_32_32), this));
+		map.getOverlays().add(
+				startMarker = new MapOverlay(getResources().getDrawable(
+						R.drawable.blue_map_marker_32_32), this));
+
+		mAcceler = new float[3];
+		mMagnetic = new float[3];
+
+		if (savedInstanceState != null) {
+			Echo.d(this, "restore bundle=" + savedInstanceState.size());
+			mDistance = savedInstanceState.getInt(BUND_DIST);
+			mDistanceTraveled = savedInstanceState.getDouble(BUND_DIST_TRAV);
+			mBearing = savedInstanceState.getDouble(BUND_BEAR);
+			mStartLocation = savedInstanceState.getParcelable(BUND_STARTLOC);
+			mDestLocation = savedInstanceState.getParcelable(BUND_DESTLOC);
+			isCalibrated = true;
+
+		} else {
+			Bundle extras = getIntent().getExtras();
+			mDistance = extras.getInt(SelectDistanceActivity.BUNDLE_DISTANCE, 0);
+
+			// get a fresh start, only concerns are time & accuracy
+			Time time = new Time();
+			time.setToNow();
+			long now = time.toMillis(false);
+			long seconds;
+
+			lkGPS = mLM.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+			// seconds = (lkGPS.getTime() - now) / 1000;
+			// greenMarker.addLocation(lkGPS, "LK GPS",
+			// "Time=" + seconds + "\n" + lkGPS.toString());
+
+			// time freshness must < 5 minutes & accuracy must < 100m
+			if (lkGPS != null && now - lkGPS.getTime() < LOCATION_TIME_THRESH
+					&& lkGPS.getAccuracy() < LOCATION_ACC_THRESH) {
+				mStartLocation = mCurLocation = lkGPS;
+
+				startMarker.addLocation(lkGPS, "LK GPS", lkGPS.toString());
+				playerMarker.addLocation(mCurLocation, "CurLoc",
+						mCurLocation.toString());
+
+				Echo.d(this, "gps=\n" + lkGPS);
+			}
+
+			lkNetwork = mLM.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+			// seconds = (lkNetwork.getTime() - now) / 1000;
+			// greenMarker.addLocation(lkNetwork, "LK NET", "Time=" + seconds + "\n"
+			// + lkNetwork.toString());
+
+			if (lkNetwork != null
+					&& ((mCurLocation != null
+							&& lkNetwork.getTime() > mCurLocation.getTime() && lkNetwork
+							.getAccuracy() < LOCATION_ACC_THRESH) || (mCurLocation == null
+							&& now - lkNetwork.getTime() < LOCATION_TIME_THRESH && lkNetwork
+							.getAccuracy() < LOCATION_ACC_THRESH))) {
+				mStartLocation = mCurLocation = lkNetwork;
+
+				startMarker.clearOverlay();
+				startMarker.addLocation(lkNetwork, "LK NET", lkNetwork.toString());
+				playerMarker.clearOverlay();
+				playerMarker.addLocation(mCurLocation, "CurLoc",
+						mCurLocation.toString());
+
+				Echo.d(this, "network=\n" + lkNetwork);
+			}
+
+			lkPassive = mLM.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
+			// seconds = (lkPassive.getTime() - now) / 1000;
+			// greenMarker.addLocation(lkPassive, "LK NET", "Time=" + seconds + "\n"
+			// + lkPassive.toString());
+
+			if (lkPassive != null
+					&& ((mCurLocation != null
+							&& lkPassive.getTime() > mCurLocation.getTime() && lkPassive
+							.getAccuracy() < LOCATION_ACC_THRESH) || (mCurLocation == null
+							&& now - lkPassive.getTime() < LOCATION_TIME_THRESH && lkPassive
+							.getAccuracy() < LOCATION_ACC_THRESH))) {
+				mStartLocation = mCurLocation = lkPassive;
+
+				startMarker.clearOverlay();
+				startMarker.addLocation(lkNetwork, "LK PASSIVE", lkPassive.toString());
+				playerMarker.clearOverlay();
+				playerMarker.addLocation(mCurLocation, "CurLoc",
+						mCurLocation.toString());
+
+				Echo.d(this, "passive=\n" + lkPassive);
+			}
+
+			// randomize direction, calculate destination if current location is set
+			mBearing = Math.random() * 360;
+			if (mStartLocation != null) {
+				mDestLocation = Geodesy.calculateVincentyDestination(mStartLocation,
+						mBearing, mDistance);
+
+				destMarker.addLocation(mDestLocation, "Dest", mDestLocation.toString());
+
+				// tvCurLoc.setText("===curLoc===\n" + mCurLocation.toString());
+				// tvDestLoc.setText("===destLoc===\n" + mDestLocation.toString());
+				// tvDistance.setText("===dist==="
+				// + mCurLocation.distanceTo(mDestLocation));
+			} else {
+				// else calibrate device
+				// showDialog(LOCATION_LOCK_DIALOG);
+			}
+
+		}
+	}
+
+	public void onClick(View v) {
+		int id = v.getId();
+		if (id == R.id.hunt_but_showmap) {
+			Intent showmap = new Intent(this, HuntingMapActivity.class);
+			startActivity(showmap);
+
+		} else if (id == R.id.map_but_dig) {
+			if (atDest) {
+				// give user reward
+				Treasure reward = Treasure.generateReward();
+				mDB.incrementTreasure(reward, 1);
+
+				mDM.incSuccessExpeditions(1);
+
+			} else {
+				mDM.incFailedExpeditions(1);
+			}
+		}
+	}
+
+	@Override
+	protected void onResume() {
+		super.onResume();
+		// open database
+		mDB.open();
+
+		// register location updates
+		for (String provider : mLM.getAllProviders()) {
+			Leg.d("provider=" + provider);
+
+			mLM.requestLocationUpdates(provider, 0, mDistance * 0.05 < 10 ? 10
+					: (int) (mDistance * 0.05), this);
+		}
+
+		// register sensor updates
+		mSM.registerListener(this, mSM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+				SensorManager.SENSOR_DELAY_UI);
+		mSM.registerListener(this,
+				mSM.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
+				SensorManager.SENSOR_DELAY_UI);
+	}
+
+	@Override
+	protected void onPause() {
+		super.onPause();
+		Leg.d("");
+		mDB.close();
+		mLM.removeUpdates(this);
+		mSM.unregisterListener(this);
+	}
+
+	@Override
+	protected void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+
+		if (isCalibrated) {
+			outState.putInt(BUND_DIST, mDistance);
+			outState.putDouble(BUND_DIST_TRAV, mDistanceTraveled);
+			outState.putDouble(BUND_BEAR, mBearing);
+			outState.putParcelable(BUND_STARTLOC, mStartLocation);
+			outState.putParcelable(BUND_DESTLOC, mDestLocation);
+		}
+
+		Leg.d("saved bundle=" + outState.size());
+	}
+
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		getMenuInflater().inflate(R.menu.activity_main, menu);
+		return true;
+	}
+
+	@Override
+	protected Dialog onCreateDialog(int id, Bundle args) {
+
+		if (id == LOCATION_LOCK_DIALOG) {
+			pdLock.setTitle("Calibrating device");
+			pdLock.setMessage("Please walk around so that the GPS can get a fix");
+			pdLock.setCanceledOnTouchOutside(false);
+			pdLock.setOnCancelListener(new OnCancelListener() {
+				@Override
+				public void onCancel(DialogInterface dialog) {
+					finish();
+				}
+			});
+			return pdLock;
+
+		}
+
+		return super.onCreateDialog(id, args);
+	}
+
+	/********************************/
+	/** Interface LocationListener **/
+	/********************************/
+
+	@Override
+	public void onLocationChanged(Location location) {
+		Leg.d("location=" + location.toString());
+		if (location.getAccuracy() > LOCATION_ACC_THRESH) {
+			Leg.d("Discarded inaccurate location=" + location.getAccuracy());
+			return;
+		}
+
+		mCurLocation = location;
+
+		// tvCurLoc.setText("===curLoc===\n" + location.toString());
+
+		// always recalibrate on first location lock
+		if (mDestLocation == null || !isCalibrated) {
+			mStartLocation = location;
+
+			startMarker.clearOverlay();
+			startMarker.addLocation(mStartLocation, "Cali-Start",
+					mStartLocation.toString());
+
+			mDestLocation = Geodesy.calculateVincentyDestination(location, mBearing,
+					mDistance);
+
+			destMarker.clearOverlay();
+			destMarker.addLocation(mDestLocation, "Cali-Dest",
+					mDestLocation.toString());
+			// tvDestLoc.setText("===destLoc===\n" + mDestLocation.toString());
+
+			isCalibrated = true;
+			mDM.incTotalExpeditions(1);
+
+			if (pdLock.isShowing()) {
+				pdLock.dismiss();
+			}
+		}
+
+		// log traveled distance
+		if (mLKLocation != null) {
+			float traveled = mLKLocation.distanceTo(mCurLocation);
+			mDistanceTraveled += traveled;
+			mDM.incDistanceTraveled(traveled);
+		}
+		mLKLocation = mCurLocation;
+
+		// determine if player is close enough to win
+		if (mCurLocation.distanceTo(mDestLocation) < (float) (mDistance * 0.05)) {
+			atDest = true;
+			Treasure treasure = Treasure.generateReward();
+
+			mDB.incrementTreasure(treasure, 1);
+			mDB.incrementExpeditions(1, 1);
+			mDB.incrementDistance(1, mDistanceTraveled);
+		}
+
+		playerMarker.clearOverlay();
+		playerMarker.addLocation(mCurLocation, "CurLoc", mCurLocation.toString());
+
+		// tvDistance.setText("===dist===" +
+		// mCurLocation.distanceTo(mDestLocation));
+	}
+
+	@Override
+	public void onProviderDisabled(String provider) {
+	}
+
+	@Override
+	public void onProviderEnabled(String provider) {
+
+	}
+
+	@Override
+	public void onStatusChanged(String provider, int status, Bundle extras) {
+		Echo.d(this, "provider=" + provider + " status=" + status + " extras="
+				+ extras.toString());
+	}
+
+	/***********************************/
+	/** Interface SensorEventListener **/
+	/***********************************/
+
+	@Override
+	public void onAccuracyChanged(Sensor sensor, int accuracy) {
+	}
+
+	@Override
+	public void onSensorChanged(SensorEvent event) {
+		int type = event.sensor.getType();
+		if (type == Sensor.TYPE_ACCELEROMETER) {
+			System.arraycopy(event.values, 0, mAcceler, 0, 3);
+		} else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
+			System.arraycopy(event.values, 0, mMagnetic, 0, 3);
+		}
+
+		float[] r = new float[9], bearing = new float[3];
+		if (SensorManager.getRotationMatrix(r, null, mAcceler, mMagnetic)) {
+			SensorManager.getOrientation(r, bearing);
+			// tvBearing.setText(String.format("x=%.5f y=%.5f z=%.5f", bearing[0],
+			// bearing[1], bearing[2]));
+		}
+	}
+
+	/****************************/
+	/** Base class MapActivity **/
+	/****************************/
+
+	@Override
+	protected boolean isRouteDisplayed() {
+		return false;
+	}
+
+}

File src/nctuw/littledot/localtreasure/ExpeditionCompleteActivity.java

+package nctuw.littledot.localtreasure;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class ExpeditionCompleteActivity extends Activity {
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+	}
+
+	@Override
+	protected void onResume() {
+		super.onResume();
+	}
+
+	@Override
+	protected void onPause() {
+		super.onPause();
+	}
+
+}

File src/nctuw/littledot/localtreasure/ExpeditionListener.java

+package nctuw.littledot.localtreasure;
+
+import android.location.Location;
+
+public interface ExpeditionListener {
+	void onDestinationArrival();
+
+	void onLocationChanged(Location location);
+}

File src/nctuw/littledot/localtreasure/ExpeditionManager.java

+package nctuw.littledot.localtreasure;
+
+import nctuw.littledot.util.Echo;
+import nctuw.littledot.util.Leg;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler.Callback;
+import android.text.format.Time;
+
+public class ExpeditionManager implements LocationListener, SensorEventListener {
+	public static final long		LOCATION_TIME_THRESH	= 5 * 60 * 1000;
+	public static final float		LOCATION_ACC_THRESH		= 50F;
+
+	private Context							mContext;
+	private LocationManager			mLM;
+	private SensorManager				mSM;
+
+	private Callback						mCallback;
+	private ExpeditionListener	mExpListener;
+
+	private int									mDistance;
+	private double							mBearing;
+	private Location						mCurLocation;
+	private Location						mDestLocation;
+	private boolean							mRecalibrate					= true;
+	private boolean							atDest								= false;
+
+	private float								mAcceler[];
+	private float								mMagnetic[];
+
+	private Location						lkGPS;
+	private Location						lkNetwork;
+	private Location						lkPassive;
+
+	public ExpeditionManager(Context context, int distance) {
+		mContext = context;
+		mDistance = distance;
+	}
+
+	public ExpeditionManager(Context context, int distance, double bearing,
+			Location dest) {
+		mContext = context;
+		mDistance = distance;
+		mBearing = bearing;
+	}
+
+	private void init() {
+		// get a fresh start, only concerns are time & accuracy
+		Time time = new Time();
+		time.setToNow();
+		long now = time.toMillis(false);
+		long seconds;
+
+		lkGPS = mLM.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+
+		// time freshness must < 5 minutes & accuracy must < 50m
+		if (lkGPS != null && now - lkGPS.getTime() < LOCATION_TIME_THRESH
+				&& lkGPS.getAccuracy() < LOCATION_ACC_THRESH) {
+			mCurLocation = lkGPS;
+			Echo.d(mContext, "gps=\n" + lkGPS);
+		}
+
+		lkNetwork = mLM.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+
+		if (lkNetwork != null
+				&& ((mCurLocation != null
+						&& lkNetwork.getTime() > mCurLocation.getTime() && lkNetwork
+						.getAccuracy() < LOCATION_ACC_THRESH) || (mCurLocation == null
+						&& now - lkNetwork.getTime() < LOCATION_TIME_THRESH && lkNetwork
+						.getAccuracy() < LOCATION_ACC_THRESH))) {
+			mCurLocation = lkNetwork;
+			Echo.d(mContext, "network=\n" + lkNetwork);
+		}
+
+		lkPassive = mLM.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
+
+		if (lkPassive != null
+				&& ((mCurLocation != null
+						&& lkPassive.getTime() > mCurLocation.getTime() && lkPassive
+						.getAccuracy() < LOCATION_ACC_THRESH) || (mCurLocation == null
+						&& now - lkPassive.getTime() < LOCATION_TIME_THRESH && lkPassive
+						.getAccuracy() < LOCATION_ACC_THRESH))) {
+			mCurLocation = lkPassive;
+			Echo.d(mContext, "passive=\n" + lkPassive);
+		}
+
+		// randomize direction, calculate destination if current location is set
+		mBearing = Math.random() * 360;
+		if (mCurLocation != null) {
+			mDestLocation = Geodesy.calculateVincentyDestination(mCurLocation,
+					mBearing, mDistance);
+		}
+
+		// register location updates
+		for (String provider : mLM.getAllProviders()) {
+			Leg.d("provider=" + provider);
+
+			mLM.requestLocationUpdates(provider, 0, mDistance * 0.05 < 10 ? 10
+					: (int) (mDistance * 0.05), this);
+		}
+
+		// register sensor updates
+		mSM.registerListener(this, mSM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+				SensorManager.SENSOR_DELAY_UI);
+		mSM.registerListener(this,
+				mSM.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
+				SensorManager.SENSOR_DELAY_UI);
+	}
+
+	public void setOnDestinationArrivalListener(Callback callback) {
+		mCallback = callback;
+	}
+
+	/********************************/
+	/** Interface LocationListener **/
+	/********************************/
+
+	@Override
+	public void onLocationChanged(Location location) {
+		Leg.d("location=" + location.toString());
+		if (location.getAccuracy() > LOCATION_ACC_THRESH) {
+			Leg.d("Discarded inaccurate location=" + location.getAccuracy());
+			return;
+		}
+
+		mCurLocation = location;
+
+		// always recalibrate on first gps lock
+		if (mDestLocation == null || mRecalibrate) {
+			mDestLocation = Geodesy.calculateVincentyDestination(location, mBearing,
+					mDistance);
+			// greenMarker.addLocation(mDestLocation, "recali-Dest",
+			// mDestLocation.toString());
+			// tvDestLoc.setText("===destLoc===\n" + mDestLocation.toString());
+
+			mRecalibrate = false;
+
+			// if (pdLock.isShowing()) {
+			// pdLock.dismiss();
+			// }
+		}
+
+		if (mCurLocation.distanceTo(mDestLocation) < (float) (mDistance * 0.05)) {
+			atDest = true;
+			// greenMarker.addLocation(mCurLocation, "CurLoc",
+			// mCurLocation.toString());
+		} else if (mCurLocation.getProvider().equals(LocationManager.GPS_PROVIDER)) {
+			// blueMarker.addLocation(mCurLocation, "CurLoc",
+			// mCurLocation.toString());
+		} else {
+			// pinkMarker.addLocation(mCurLocation, "CurLoc",
+			// mCurLocation.toString());
+		}
+		// tvDistance.setText("===dist===" +
+		// mCurLocation.distanceTo(mDestLocation));
+	}
+
+	@Override
+	public void onProviderDisabled(String provider) {
+
+	}
+
+	@Override
+	public void onProviderEnabled(String provider) {
+
+	}
+
+	@Override
+	public void onStatusChanged(String provider, int status, Bundle extras) {
+	}
+
+	/***********************************/
+	/** Interface SensorEventListener **/
+	/***********************************/
+
+	@Override
+	public void onAccuracyChanged(Sensor sensor, int accuracy) {
+	}
+
+	@Override
+	public void onSensorChanged(SensorEvent event) {
+		int type = event.sensor.getType();
+		if (type == Sensor.TYPE_ACCELEROMETER) {
+			System.arraycopy(event.values, 0, mAcceler, 0, 3);
+		} else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
+			System.arraycopy(event.values, 0, mMagnetic, 0, 3);
+		}
+
+		float[] r = new float[9], bearing = new float[3];
+		if (SensorManager.getRotationMatrix(r, null, mAcceler, mMagnetic)) {
+			SensorManager.getOrientation(r, bearing);
+			// tvBearing.setText(String.format("x=%.5f y=%.5f z=%.5f", bearing[0],
+			// bearing[1], bearing[2]));
+		}
+	}
+
+}

File src/nctuw/littledot/localtreasure/MapOverlay.java

 import android.app.AlertDialog;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.location.Location;
 
+import com.google.android.maps.GeoPoint;
 import com.google.android.maps.ItemizedOverlay;
 import com.google.android.maps.OverlayItem;
 
 	private Context									mContext;
 
 	public MapOverlay(Drawable defaultMarker) {
-		super(boundCenterBottom(defaultMarker	));
+		super(boundCenterBottom(defaultMarker));
+		populate();
 	}
 
 	public MapOverlay(Drawable defaultMarker, Context context) {
 		super(boundCenterBottom(defaultMarker));
 		mContext = context;
+		populate();
+	}
+
+	public void addLocation(Location location, String title, String snippet) {
+		GeoPoint point;
+
+		if (location != null) {
+			point = new GeoPoint((int) (location.getLatitude() * 1000000F),
+					(int) (location.getLongitude() * 1000000F));
+			OverlayItem overlayitem = new OverlayItem(point, title, snippet);
+			addOverlay(overlayitem);
+		}
 	}
 
+	public void clearOverlay() {
+		mOverlays.clear();
+		populate();
+	}
+
+	/*
+	 * Class ItemizedOverlay (non-Javadoc)
+	 * @see com.google.android.maps.ItemizedOverlay#size()
+	 */
+
 	@Override
 	public int size() {
 		return mOverlays.size();

File src/nctuw/littledot/localtreasure/SelectDistanceActivity.java

 	}
 
 	public void onChooseDistance(View v) {
-		Intent intent = new Intent(this, HuntingActivity.class);
+		Intent intent = new Intent(this, ExpeditionActivity.class);
 
 		switch (v.getId()) {
 			case R.id.distance_but_500m:
 			et.setVisibility(View.VISIBLE);
 			but.setText("OK!");
 		} else {
-			Intent intent = new Intent(this, HuntingActivity.class);
+			Intent intent = new Intent(this, ExpeditionActivity.class);
 			intent.putExtra(BUNDLE_DISTANCE,
 					Integer.parseInt(et.getText().toString()));
 			startActivity(intent);

File src/nctuw/littledot/localtreasure/Treasure.java

+package nctuw.littledot.localtreasure;
+
+import java.util.Random;
+
+public enum Treasure {
+	APPLE(R.drawable.treasure_apple_256, "Apple"),
+	ORANGE(R.drawable.treasure_orange_256, "Orange"),
+	BANANA(R.drawable.treasure_banana_256, "Banana");
+
+	private final int			drawableID;
+	private final String	name;
+
+	Treasure(int drawID, String n) {
+		drawableID = drawID;
+		name = n;
+	}
+
+	public int getDrawableId() {
+		return drawableID;
+	}
+
+	/**
+	 * Generates a random reward.
+	 * 
+	 * @return
+	 */
+	public static Treasure generateReward() {
+		Random rand = new Random();
+		int index = rand.nextInt(values().length);
+
+		return values()[index];
+	}
+}