littledot5566 avatar littledot5566 committed 37f4001

RISING FROM THE ASHES.

Comments (0)

Files changed (90)

+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>LocalTreasure</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

AndroidManifest.xml

+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="nctuw.littledot.localtreasure"
+    android:versionCode="5"
+    android:versionName="0.15" >
+
+    <uses-sdk
+        android:minSdkVersion="7"
+        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.INTERNET" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/treasure_chest_512"
+        android:label="Local Treasure"
+        android:theme="@style/Holo.Theme.Sherlock.MediumText" >
+
+        <!--  -->
+        <uses-library android:name="com.google.android.maps" />
+
+        <activity
+            android:name=".components.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=".components.ExpeditionActivity"
+            android:configChanges="keyboardHidden|orientation"
+            android:screenOrientation="portrait" >
+        </activity>
+        <!--  -->
+        <activity
+            android:name=".ExpeditionCompleteActivity"
+            android:configChanges="keyboardHidden|orientation"
+            android:screenOrientation="portrait" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.MainActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.ExpeditionConfigActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.ProfileActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.TestBundleActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.HistoryActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.TreasureActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.SettingsActivity" >
+        </activity>
+        <!--  -->
+        <activity android:name=".components.CreditsActivity" >
+        </activity>
+    </application>
+
+</manifest>
Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+</lint>

proguard-project.txt

+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+-libraryjars ../HoloEverywhere/library/bin/he.jar
+-libraryjars ../ActionBarSherlock/library/bin/abs.jar
+-libraryjars ../Utils/bin/utils.jar
+#-libraryjars E:\android-sdk-linux\add-ons\addon-google_apis-google-16\libs\maps.jar
+
+-keep class com.actionbarsherlock.** {*;}
+-keep class org.holoeverywhere.** {*;}
+#-keep class com.google.** {*;}
+-assumenosideeffects class nctuw.littledot.util.Leg {*;}
+
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

project.properties

+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=proguard-project.txt
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
+
+# Project target.
+target=Google Inc.:Google APIs:16
+android.library.reference.1=../Utils
+android.library.reference.2=../HoloEverywhere/library
+android.library.reference.3=../ActionBarSherlock
Add a comment to this file

release/LocalTreasure.apk

Binary file added.

Add a comment to this file

release/LocalTreasure_v0.15.apk

Binary file added.

+> sqlite select query where values must follow "=" with no spaces.
+X: select * from profile where pID = 1;
+O: select * from profile where pID=1;
+
+> Android SQLiteDatabase.rawQuery() cannot substitue table names and column names with "?".
+X: db.rawQuery("select * from ? where ?=?", new String[]{"profile", "pID", "1"});
+O: db.rawQuery(String.format("select * from %s where %s=%d", "profile", "pID", 1));
+
+> How to find the limits for Floats and Doubles?
+O: Use $(Type).MAX_VALUE to check (eg: Float.MAX_VALUE, Double.MAX_VALUE)
+
+O: ABS and HE can share HE's SharedPreferences, but not compatible with Android's SharedPreferences.
+v Continue active expedition.
+v Expedition list in ListView form.
+v delete expedition from history.
+* Help/About/Credits button telling users what this app is about, thanks for icons, etc.
+* Expedition statistics. Graph of travelled distance over days, etc.
+v Display treasure in a trophy case.
+* Distance from destination, distance travelled in MapAct.
+* "Displayable" column for Expeditions. Don't delete data. (do last)
+v After completing an Expedition, remove from Active Expedition.
+v Completed Expeditions should not display "Continue Expedition" option.
+* Back button instead of icon.
+* Different excuses when the log is empty. "Had a cold..." "It was raining..." etc.
+* Implement sonar sounds when approaching treasure so that user doesn't have to look at the screen all the time.
+* To-and-fro vs One-way mode
+* Freedom mode
+* Multiple-destination mode.
+* Add expedition info in ExpeditionAcitivty. Use icon in action bar to trigger.

release/treasure-hunter-ideas.txt

+Android Application Treasure Hunter Ideas
+
+The pitch:
+
+Treasure Hunter is an app that promotes exercise by getting users out of their seats!
+For to long have users been stranded inside their houses, we're here to save them from their couches!
+
+The idea behind Treasure Hunter is similar to the childhood game Hot-Warm-Cold, where the protagonist, who does not know the location of the treasure, must depend on the other participants in the game to guide him/her by giving hints of either "hotter" or "colder".
+
+In this app, the user first sets a distance bracket of how much he/she would like to travel. The application then randomly generates a destination where the user must travel to to unveil his treasures.
+
+(Ex1) Distance brackets:
+1-100m, 100-300m, 300-500m, 500-1km, 1-2km, etc.
+
+Once the destination has been set and the user can proceed, the screen will display the distance between the user and the user's destination. As the user approaches the destination, the distance shrinks. Until a reasonable threshold (say 10% of the total distance to travel) is reached, the app notifies that the user is close enough and displays a "collect treasure" button like those cheesy phishing websites. The user finally taps the button to claim his hard-earned prize.
+
+(Ex2) The "collect treasure" button
++--------------+
+|Start Digging!|
++--------------+
+
+This is just what we call "soft-core" mode. The user knows the exact distance to travel. We can kick that up a notch by introducing "hard-core" mode. Instaed of showing the exact distance, we only display keywords such as "arctic ice", "freezing cold", "cool", "warm", "hot", "burning sun", etc. as the user travels. Some problems could arise with this: if the target distance is super far (like 5km) and there are not enough identifiers (say 5), each identifier would have to span 1km, that would leave the user clueless where to go. Ideally, each identifier should span 100-200m so that users wouldn't be left walking in vain, but isn't that the point in the first place, walking for a virtual reward?
+
+We can also kick it down a notch by creating "kiddie-mode", which basically includes a map and routes the kids' travel path.
+
+(Ex3) Hard-core mode
++---------------------------------+
+|Brr... It's freezing cold here...|
+|Let's start moving...            |
+|(no distance shown at all)       |
++---------------------------------+
+
+In some cases, the location might be private property which the user cannot trespass, or on an island where you must swim accross. We're not that hard-core, so in cases where the destination cannot be reached, we offer an exit strategy so that your progress will not go into the drain. Accompanying the "collect treasure" button, we provide a "dig now" button that is shown at all times. The "dig now" button allows the user to dig for treasure where the user is, but of course the "value" of the prize will be at a discount relative to the distance travelled. So if the user were outside a private company building where the user's destination is and can't get in, tap the "dig now" button and collect the prize, which shouldn't be too shabby compared to the full prize because the user was almost there.
+
+(Ex4) Unreachable destinations
++---------------------------+
+|I'll settle for digging now|
++---------------------------+
+
+Some people have negative opinions of where their destination. It might be a morgue, a cemetary, a nuclear power plant, etc. In these cases, we provide another exit strategy, an "I don't want to go there" button. The user is also rewarded accordingly similar to the unreachable case, but the application takes note of this decision and will never include the frowned-upon location in the pool of possible destination again.
+
+(Ex5) Scary/Dangerous destinations
++------------------------------------------+
+|This place is creepy... Get me outta here!|
++------------------------------+-----------+
+|It's too dangerous to proceed!|
++------------------------------+
+
+
+Business model:
+
+This app can bring people to places, which means it can bring users/customers straight to your  door. Say you have a huge sale, you advertise using the traditional methods, but customers don't always go to your event, especially if you are a local shop owner. With this app, we can send customers your way.
+Once there, give the user incentives to Tweet or share on Facebook by offering an extra prize or something, now you get more publicity, and it is through friends!
+
+
+
+Roadblocks:
+
+Data, data, data. Need location data of destinations. Google Places? OpenStreetMaps?
+Android, Android, Android. Need to master Location & GPS protocols. Phone power consumption is also a great concern.
Add a comment to this file

res/drawable-hdpi/ic_action_search.png

Added
New image
Add a comment to this file

res/drawable-hdpi/ic_launcher.png

Added
New image
Add a comment to this file

res/drawable-ldpi/ic_launcher.png

Added
New image
Add a comment to this file

res/drawable-mdpi/ic_action_search.png

Added
New image
Add a comment to this file

res/drawable-mdpi/ic_launcher.png

Added
New image
Add a comment to this file

res/drawable-xhdpi/ic_action_search.png

Added
New image
Add a comment to this file

res/drawable-xhdpi/ic_launcher.png

Added
New image
Add a comment to this file

res/drawable/androidmarker.png

Added
New image
Add a comment to this file

res/drawable/blue_map_marker_32_32.png

Added
New image
Add a comment to this file

res/drawable/green_map_marker_32_32.png

Added
New image
Add a comment to this file

res/drawable/green_map_marker_64_64.png

Added
New image
Add a comment to this file

res/drawable/item_iron_ore_24_24.png

Added
New image
Add a comment to this file

res/drawable/item_leather_24_24.png

Added
New image
Add a comment to this file

res/drawable/item_wood_24_24.png

Added
New image
Add a comment to this file

res/drawable/pink_map_marker_32_32.png

Added
New image
Add a comment to this file

res/drawable/treasure_apple_256.png

Added
New image
Add a comment to this file

res/drawable/treasure_banana_256.png

Added
New image
Add a comment to this file

res/drawable/treasure_chest_512.png

Added
New image
Add a comment to this file

res/drawable/treasure_orange_256.png

Added
New image

res/layout/bundletest_act_layout.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/bundle_tv_debug"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" />
+
+</LinearLayout>

res/layout/credits_act.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/credits_tv_thx"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:text="@string/credits" />
+
+    <TextView
+        android:id="@+id/credits_tv_issues"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:text="@string/report_issues" />
+
+</LinearLayout>

res/layout/expedition_act.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    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="0dp"
+        android:layout_weight="1"
+        android:apiKey="0-0tVBYzo78F-qe7esdacwXkvQC1qjv3vYvBkbA"
+        android:clickable="true" />
+    <!-- Ubuntu:		0-0tVBYzo78GcruEETjNXCnXjt4rzTKIkzU5HAQ -->
+    <!-- Win7:			0-0tVBYzo78FEXFOBhiBi06dI13u00gCOfQnEUA -->
+    <!-- android-dev:	0-0tVBYzo78F-qe7esdacwXkvQC1qjv3vYvBkbA -->
+
+</LinearLayout>

res/layout/expeditionconfig_act.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <CheckBox
+            android:id="@+id/config_cb_oneway"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:checked="true"
+            android:onClick="onClick"
+            android:text="One-way" />
+
+        <CheckBox
+            android:id="@+id/config_cb_toandfro"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:checked="false"
+            android:onClick="onClick"
+            android:text="To-and-fro" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/distance_but_500m"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onChooseDistance"
+        android:text="@string/distance_but_500m" />
+
+    <Button
+        android:id="@+id/distance_but_1000m"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onChooseDistance"
+        android:text="1000m" />
+
+    <LinearLayout
+        android:id="@+id/distance_ll_custom"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal" >
+
+        <EditText
+            android:id="@+id/distance_et_custom"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:hint="@string/distance_et_custom"
+            android:inputType="numberDecimal" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="onChooseCustomDistance"
+            android:text="Go!" />
+    </LinearLayout>
+
+</LinearLayout>

res/layout/geolock_dia_lay.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/geolock_tv_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/geolock_tv_title" />
+
+    <LinearLayout
+        android:id="@+id/geolock_ll_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <ProgressBar
+            android:id="@+id/geolock_pb_circle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/geolock_tv_content"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+</LinearLayout>

res/layout/history_act_layout.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/history_tv_empty"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="The winds brush softly across my cheek...\nIt is a fine day for an adventure!"
+        android:visibility="gone" />
+
+    <ListView
+        android:id="@+id/history_lv_expeditions"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+</LinearLayout>

res/layout/history_item.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Travelled Distance:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_travelled"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Expedition Distance:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_distance"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Remaining Distance:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_distleft"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Explorer:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_user"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Start:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_stTime"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="End:" />
+
+        <TextView
+            android:id="@+id/hItem_tv_endTime"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="Treasure:" />
+
+        <ImageView
+            android:id="@+id/hItem_iv_treasure"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/hItem_tv_treasure"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="right" />
+    </LinearLayout>
+
+</LinearLayout>

res/layout/hunting_act_layout.xml

+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    tools:context=".MainActivity" >
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@+id/hunt_tv_cur_location"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/hunt_tv_dest_location"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/hunt_tv_distance"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/hunt_tv_bearing"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <Button
+            android:id="@+id/hunt_but_showmap"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="onClick" />
+    </LinearLayout>
+
+</ScrollView>

res/layout/main_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:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/main_tv_info"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:visibility="gone" />
+
+    <Button
+        android:id="@+id/main_but_startExpedition"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Start a new treasure expedition!" />
+
+    <Button
+        android:id="@+id/main_but_continue"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Continue expedition" />
+
+    <Button
+        android:id="@+id/main_but_profile"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="View my profile" />
+
+    <Button
+        android:id="@+id/main_but_history"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Journal" />
+
+    <Button
+        android:id="@+id/main_but_chest"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Treasure Chest" />
+
+    <Button
+        android:id="@+id/main_but_settings"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Settings" />
+
+    <Button
+        android:id="@+id/main_but_credits"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:onClick="onClick"
+        android:text="Credits" />
+
+</LinearLayout>

res/layout/profile_activity.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/profile_tv_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <EditText
+        android:id="@+id/profile_et_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:inputType="text"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/profile_tv_totalExpedition"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/profile_tv_totalDistance"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/profile_tv_totalTime"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <GridView
+        android:id="@+id/profile_gv_treasure"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>

res/layout/splash_act_layout.xml

+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <ImageView
+        android:id="@+id/splash_iv_icon"
+        android:layout_width="128dp"
+        android:layout_height="128dp"
+        android:layout_centerInParent="true"
+        android:src="@drawable/treasure_chest_512" />
+
+    <TextView
+        android:id="@+id/splash_tv_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/splash_iv_icon"
+        android:layout_centerHorizontal="true"
+        android:text="Local Treasure" />
+
+</RelativeLayout>

res/layout/treasure_act.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/chest_tv_empty"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="The chest is empty..."
+        android:visibility="gone" />
+
+    <ListView
+        android:id="@+id/chest_lv_frame"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+</LinearLayout>

res/layout/treasure_item.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal" >
+
+    <ImageView
+        android:id="@+id/tItem_iv_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/tItem_tv_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" />
+
+    <TextView
+        android:id="@+id/tItem_tv_amount"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>

res/menu/activity_main.xml

+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/menu_settings"
+        android:title="@string/menu_settings"
+        android:orderInCategory="100" />
+</menu>

res/values-large/dimens.xml

+<resources>
+
+    <dimen name="padding_small">8dp</dimen>
+    <dimen name="padding_medium">16dp</dimen>
+    <dimen name="padding_large">16dp</dimen>
+
+</resources>

res/values-v11/styles.xml

+<resources>
+
+    <style name="AppTheme" parent="@android:Theme.Holo.Light" />
+
+</resources>

res/values/colors.xml

+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!-- <color name="and_">#</color> -->
+    <color name="and_blue">#33B5E5</color>
+    <color name="and_darkblue">#0099CC</color>
+    <color name="and_purple">#AA66CC</color>
+    <color name="and_darkpurple">#9933CC</color>
+    <color name="and_green">#99CC00</color>
+    <color name="and_darkgreen">#669900</color>
+    <color name="and_orange">#FFBB33</color>
+    <color name="and_darkorange">#FF8800</color>
+    <color name="and_red">#FF4444</color>
+    <color name="and_darkred">#CC0000</color>
+
+</resources>

res/values/dimens.xml

+<resources>
+
+    <dimen name="padding_small">8dp</dimen>
+    <dimen name="padding_medium">8dp</dimen>
+    <dimen name="padding_large">16dp</dimen>
+
+</resources>

res/values/strings.xml

+<resources>
+
+    <string name="app_name">Local Treasure</string>
+    <string name="menu_settings">Settings</string>
+    <string name="title_activity_main">Local Treasure</string>
+    <string name="geolock_tv_title">Discovering your current location</string>
+    <string name="main_but_startHunt">Start a new treasure expedition!</string>
+    <string name="distance_tv_title">Set your distance!</string>
+    <string name="distance_but_500m">500m</string>
+    <string name="distance_et_custom">Enter custom distance (m)</string>
+    <string name="distance_but_custom">Custom</string>
+    <!-- CreditsActivity -->
+    <string name="twitter">https://twitter.com/GGLocalTreasure</string>
+    <string name="bitbucket">https://bitbucket.org/littledot5566/localtreasure/issues</string>
+    <string name="mohsenfakharian">http://mohsenfakharian.deviantart.com/gallery</string>
+    <string name="iconsland">"http://www.icons-land.com"</string>
+    <string name="credits">Created by Littledot.\n
+        Thanks to <a href="http://mohsenfakharian.deviantart.com/gallery">Mohsen Fakharian</a> for logo.\n
+        Thanks to <a href="http://www.icons-land.com">Icons-Land</a> for icons.</string>
+    <string name="report_issues">If you uncover a bug or have suggestions for 
+        enhancement, please let me know on <a href="https://twitter.com/GGLocalTreasure">Twitter</a> or <a href="https://bitbucket.org/littledot5566/localtreasure/issues">BitBucket</a>.</string>
+
+</resources>

res/values/styles.xml

+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="AppTheme" parent="android:Theme.Light.NoTitleBar" />
+
+    <style name="Holo.TextView.MediumText" parent="Holo.TextView">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+    </style>
+
+    <style name="Holo.Theme.Sherlock.MediumText" parent="Holo.Theme">
+        <item name="android:textViewStyle">@style/Holo.TextView.MediumText</item>
+    </style>
+
+</resources>

res/xml/settings_act.xml

+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:holo="http://schemas.android.com/apk/res-auto" >
+
+    <CheckBoxPreference
+        holo:defaultValue="true"
+        holo:key="settings_exitDialog"
+        holo:summary="Show exit dialog when you press the back button in the main menu"
+        holo:title="Enable Exit Dialog" />
+
+</PreferenceScreen>

src/nctuw/littledot/localtreasure/Const.java

+package nctuw.littledot.localtreasure;
+
+import org.holoeverywhere.R;
+
+public class Const {
+	public static final String PACKAGE = "nctuw.littledot.localtreasure";
+	public static final int THEME = R.style.Theme_Sherlock_Light_DarkActionBar;
+
+	public static final String SETTINGS_EXIT_DIALOG = "settings_exitDialog";
+
+	public static final String BUNDLE_SOURCE = "source";
+	public static final String BUNDLE_EID = "eID";
+	public static final String BUNDLE_PROFILE = "userProfile";
+	public static final String BUNDLE_DISTANCE = "distance";
+}

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();
+	}
+
+}

src/nctuw/littledot/localtreasure/ExpeditionListener.java

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

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 **/
+	/********************************/
+
+	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));
+	}
+
+	public void onProviderDisabled(String provider) {
+
+	}
+
+	public void onProviderEnabled(String provider) {
+
+	}
+
+	public void onStatusChanged(String provider, int status, Bundle extras) {
+	}
+
+	/***********************************/
+	/** Interface SensorEventListener **/
+	/***********************************/
+
+	public void onAccuracyChanged(Sensor sensor, int accuracy) {
+	}
+
+	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]));
+		}
+	}
+
+}

src/nctuw/littledot/localtreasure/GPSLocationListener.java

+package nctuw.littledot.localtreasure;
+
+import android.location.Location;
+import android.location.LocationListener;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+public class GPSLocationListener implements LocationListener {
+	public static int	MSG_LOCATION_CHANGED	= 1;
+	private Handler		mHandle;
+
+	public GPSLocationListener(Handler handle) {
+		mHandle = handle;
+	}
+
+	public void onLocationChanged(Location location) {
+		// TODO Auto-generated method stub
+		Message msg = mHandle.obtainMessage(MSG_LOCATION_CHANGED, location);
+		mHandle.sendMessage(msg);
+	}
+
+	public void onProviderDisabled(String provider) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void onProviderEnabled(String provider) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void onStatusChanged(String provider, int status, Bundle extras) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

src/nctuw/littledot/localtreasure/Geodesy.java

+package nctuw.littledot.localtreasure;
+
+import org.gavaghan.geodesy.Ellipsoid;
+import org.gavaghan.geodesy.GeodeticCalculator;
+import org.gavaghan.geodesy.GlobalCoordinates;
+
+import android.location.Location;
+
+public class Geodesy {
+
+	public static Location calculateHaversineDestination(Location curLoc,
+			double bearing, double distance) {
+
+		double radLong = Math.toRadians(curLoc.getLongitude());
+		double radLat = Math.toRadians(curLoc.getLatitude());
+		double radBearing = Math.toRadians(bearing);
+
+		double earthRadius = 6371009; // meter
+		double angle = distance / earthRadius;
+
+		double destLat = Math.asin(Math.sin(radLat) * Math.cos(angle)
+				+ Math.cos(radLat) * Math.sin(angle) * Math.cos(radBearing));
+		double destLong = radLong
+				+ Math.atan2(
+						Math.sin(radBearing) * Math.sin(angle)
+								* Math.cos(radLat),
+						Math.cos(angle) - Math.sin(radLat) * Math.sin(radLat));
+
+		Location loc = new Location("TreasureHunter");
+		loc.setLatitude(destLat);
+		loc.setLongitude(destLong);
+		return loc;
+	}
+
+	public static Location calculateVincentyDestination(Location curLoc,
+			double bearing, double distance) {// meter
+		// instantiate the calculator
+		GeodeticCalculator geoCalc = new GeodeticCalculator();
+
+		// select a reference elllipsoid
+		Ellipsoid reference = Ellipsoid.WGS84;
+
+		// set Lincoln Memorial coordinates
+		GlobalCoordinates lincolnMemorial;
+		lincolnMemorial = new GlobalCoordinates(curLoc.getLatitude(),
+				curLoc.getLongitude());
+
+		// find the destination
+		double[] endBearing = new double[1];
+		GlobalCoordinates dest = geoCalc.calculateEndingGlobalCoordinates(
+				reference, lincolnMemorial, bearing, distance, endBearing);
+
+		Location destLoc = new Location("TreasureHunter");
+		destLoc.setLatitude(dest.getLatitude());
+		destLoc.setLongitude(dest.getLongitude());
+		return destLoc;
+	}
+
+}

src/nctuw/littledot/localtreasure/MapOverlay.java

+package nctuw.littledot.localtreasure;
+
+import java.util.ArrayList;
+
+import org.holoeverywhere.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;
+
+public class MapOverlay extends ItemizedOverlay<OverlayItem> {
+
+	private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
+	private Context mContext;
+
+	public MapOverlay(Drawable 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();
+	}
+
+	@Override
+	protected OverlayItem createItem(int i) {
+		// When the populate() method executes, it will call createItem(int) in the
+		// ItemizedOverlay to retrieve each OverlayItem. You must override this
+		// method to properly read from the ArrayList and return the OverlayItem
+		// from the position specified by the given integer.
+		return mOverlays.get(i);
+	}
+
+	public void addOverlay(OverlayItem overlay) {
+		mOverlays.add(overlay);
+
+		// Each time you add a new OverlayItem to the ArrayList, you must call
+		// populate() for the ItemizedOverlay, which will read each of the
+		// OverlayItem objects and prepare them to be drawn.
+
+		populate();
+	}
+
+	@Override
+	protected boolean onTap(int index) {
+		OverlayItem item = mOverlays.get(index);
+		AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
+		dialog.setTitle(item.getTitle());
+		dialog.setMessage(item.getSnippet());
+		dialog.show();
+		return true;
+	}
+
+}

src/nctuw/littledot/localtreasure/components/CreditsActivity.java

+package nctuw.littledot.localtreasure.components;
+
+import nctuw.littledot.localtreasure.R;
+
+import org.holoeverywhere.app.Activity;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.view.MenuItem;
+
+import android.os.Bundle;
+import android.text.method.LinkMovementMethod;
+import android.widget.TextView;
+
+public class CreditsActivity extends Activity {
+
+	private TextView tvThx;
+	private TextView tvIssues;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+
+		setContentView(R.layout.credits_act);
+		tvThx = (TextView) findViewById(R.id.credits_tv_thx);
+		tvIssues = (TextView) findViewById(R.id.credits_tv_issues);
+		tvThx.setMovementMethod(LinkMovementMethod.getInstance());
+		tvIssues.setMovementMethod(LinkMovementMethod.getInstance());
+
+		ActionBar ab = getSupportActionBar();
+		ab.setDisplayHomeAsUpEnabled(true);
+		ab.setDisplayShowHomeEnabled(false);
+		ab.setTitle("Credits");
+
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		int id = item.getItemId();
+
+		if (id == android.R.id.home) {
+			finish();
+			return true;
+		}
+		return super.onOptionsItemSelected(item);
+	}
+}

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

+package nctuw.littledot.localtreasure.components;
+
+import java.text.NumberFormat;
+
+import nctuw.littledot.localtreasure.Const;
+import nctuw.littledot.localtreasure.Geodesy;
+import nctuw.littledot.localtreasure.MapOverlay;
+import nctuw.littledot.localtreasure.R;
+import nctuw.littledot.localtreasure.database.DB;
+import nctuw.littledot.localtreasure.database.Expedition;
+import nctuw.littledot.localtreasure.database.SP;
+import nctuw.littledot.localtreasure.database.Treasure;
+import nctuw.littledot.util.Echo;
+import nctuw.littledot.util.Leg;
+
+import org.holoeverywhere.app.AlertDialog;
+import org.holoeverywhere.app.ProgressDialog;
+import org.holoeverywhere.widget.TextView;
+import org.holoeverywhere.widget.Toast;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+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.view.View;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockMapActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.google.android.maps.GeoPoint;
+import com.google.android.maps.MapController;
+import com.google.android.maps.MapView;
+
+public class ExpeditionActivity extends SherlockMapActivity 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 int EXPEDITION_COMPLETE_DIALOG = 0;
+	public static final int GIVE_UP_DIALOG = 2;
+	public static final int INFO_DIALOG = 3;
+
+	public static final String BUND_EXPEDITION = "expedition";
+
+	private LocationManager mLM;
+	private SensorManager mSM;
+	private DB mDB;
+	private SP mSP;
+
+	private MapView map;
+
+	private ProgressDialog pdLock;
+
+	// game states
+	private boolean isCalibrated = false;
+	private boolean isAtDest = false;
+
+	private float mAcceler[] = new float[3];
+	private float mMagnetic[] = new float[3];
+	private MapOverlay destMarker; // destination
+	private MapOverlay playerMarker; // player
+	private MapOverlay startMarker; // start
+	private MapController mController;
+
+	private Expedition mExpedition;
+
+	// private ExpeditionManager ell;
+
+	/*
+	 * Base class Activity
+	 */
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		setTheme(Const.THEME);
+		super.onCreate(savedInstanceState);
+
+		setContentView(R.layout.expedition_act);
+
+		ActionBar ab = getSupportActionBar();
+		ab.setDisplayHomeAsUpEnabled(true);
+		ab.setDisplayShowHomeEnabled(false);
+		ab.setTitle("Local Treasure");
+
+		// mLI = (LayoutInflater)
+		// getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+		mLM = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+		mSM = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+		mDB = new DB(this).open(DB.MODE_WRITE);
+		mSP = new SP(this);
+
+		// initialize map widgets
+		map = (MapView) findViewById(R.id.mapview);
+		mController = map.getController();
+
+		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));
+
+		if (savedInstanceState != null) {
+			mExpedition = savedInstanceState.getParcelable(BUND_EXPEDITION);
+			isCalibrated = true;
+			Leg.a(String.format("bundle=%s", mExpedition.toString()));
+
+		} else {
+			// resume or start a new Expedition
+			Bundle extras = getIntent().getExtras();
+			String source = extras.getString(Const.BUNDLE_SOURCE);
+
+			if (source.equals(ExpeditionConfigActivity.class.getName())) {
+				// initialize new Expedition
+				mExpedition = new Expedition();
+				mExpedition.setPID(mSP.getActiveProfileID());
+				mExpedition.setStartTS(System.currentTimeMillis());
+				mExpedition.setDistance(extras.getDouble(Const.BUNDLE_DISTANCE,
+						0));
+				mExpedition.setDistanceLeft(mExpedition.getDistance());
+
+			} else if (source.equals(MainActivity.class.getName())
+					|| source.equals(HistoryActivity.class.getName())) {
+				// load saved Expedition
+				long eID = extras.getLong(Const.BUNDLE_EID);
+				mExpedition = mDB.queryExpedition(eID);
+
+				startMarker.clearOverlay();
+				startMarker
+						.addLocation(
+								mExpedition.getStartLoc(),
+								"Start",
+								"Reach the destination and claim your prize.\nBe careful of heavy traffic though, safety first!");
+				destMarker.clearOverlay();
+				destMarker.addLocation(mExpedition.getDestLoc(), "Destination",
+						"There seems to be something shimmering over there!");
+
+				mController.setCenter(new GeoPoint(
+						(int) (mExpedition.getDestLoc().getLatitude() * 1E6),
+						(int) (mExpedition.getDestLoc().getLongitude() * 1E6)));
+				mController.setZoom(14);
+
+				isCalibrated = true;
+
+				Toast.makeText(this,
+						"Please walk around so that the GPS can get a fix",
+						Toast.LENGTH_LONG).show();
+			}
+			Leg.a(mExpedition.toString());
+
+			if (!isCalibrated)
+				showDialog(LOCATION_LOCK_DIALOG);
+
+			/*
+			 * // 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.a(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.a(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.a(this, "passive=\n" + lkPassive); }
+			 */
+
+			// 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);
+			// }
+
+		}
+	}
+
+	@Override
+	protected void onResume() {
+		super.onResume();
+		// open database
+		mDB.open(DB.MODE_WRITE);
+
+		// register location updates
+		for (String provider : mLM.getAllProviders()) {
+			Leg.a("provider=" + provider);
+
+			mLM.requestLocationUpdates(provider, 3000, 0, 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.a("[exp]:" + mExpedition);
+
+		// mExpedition.setEndTS(System.currentTimeMillis());
+
+		// save only after destination is confirmed
+		if (mExpedition.getDestLoc() != null) {
+			mDB.updateExpedition(mExpedition);
+			// only for incompleted expeditions
+			if (!isAtDest)
+				mSP.setActiveExpeditionID(mExpedition.getEID());
+		}
+
+		mDB.close();
+		mLM.removeUpdates(this);
+		mSM.unregisterListener(this);
+	}
+
+	@Override
+	protected void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		if (isCalibrated) {
+			outState.putParcelable(BUND_EXPEDITION, mExpedition);
+		}
+
+		Leg.a(String.format("expedition=%s", mExpedition.toString()));
+	}
+
+	public void onClick(View v) {
+		int id = v.getId();
+	}
+
+	private static int MENU_GIVEUP = 1;
+	private static int MENU_INFO = 2;
+
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+
+		menu.add(Menu.NONE, MENU_INFO, Menu.NONE, "Info")
+				.setIcon(android.R.drawable.ic_menu_info_details)
+				.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
+		menu.add(Menu.NONE, MENU_GIVEUP, Menu.NONE, "Edit")
+				.setIcon(android.R.drawable.ic_menu_myplaces)
+				.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
+
+		return super.onCreateOptionsMenu(menu);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		int id = item.getItemId();
+		if (id == android.R.id.home) {
+			finish();
+			return true;
+		} else if (id == MENU_GIVEUP) {
+			showDialog(GIVE_UP_DIALOG);
+		} else if (id == MENU_INFO) {
+			showDialog(INFO_DIALOG);
+		}
+		return super.onOptionsItemSelected(item);
+	}
+
+	@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.setCancelable(true);
+			pdLock.setOnCancelListener(new OnCancelListener() {
+				public void onCancel(DialogInterface dialog) {
+					dialog.dismiss();
+					finish();
+				}
+			});
+			return pdLock;
+
+		} else if (id == EXPEDITION_COMPLETE_DIALOG) {
+			long seconds = (mExpedition.getEndTS() - mExpedition.getStartTS()) / 1000;
+
+			String message;
+			if (mExpedition.getPrize() != null) {
+				message = String
+						.format(
+								"You have come a long way to complete this expedition!\n\n"
+										+ "You have found some %s in your travels.\n\n"
+										+ "Distance travelled: %f meters\n"
+										+ "Time taken: %d seconds\n",
+								mExpedition.getPrize().getName(),
+								mExpedition.getTravelled(),
+								seconds);
+			}
+			else {
+				message = String
+						.format(
+								"You have come a long way to complete this expedition!\n\n"
+										+ "Distance travelled: %f meters\n"
+										+ "Distance from destination: %f meters\n"
+										+ "Time taken: %d seconds\n",
+								mExpedition.getTravelled(),
+								mExpedition.getDistanceLeft(), seconds);
+			}
+
+			// Use the Builder class for convenient dialog construction
+			AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+			builder.setTitle("Congratulations!")
+					.setMessage(message)
+					.setPositiveButton("OK",
+							new DialogInterface.OnClickListener() {
+								public void onClick(DialogInterface dialog,
+										int id) {
+									dialog.dismiss();