[PATCH] Ticket #423 - Import GPX file from SD card

Venkatesh Shukla IIT BHU venkatesh.shukla.eee11 at iitbhu.ac.in
Sun Mar 9 00:04:29 PST 2014


Framework added for importing GPX file from SD card.
It works for dive points stored as Waypoints in the GPX file.
Dives are sent to the server as per preferences.

For importing GPX file
1. Click on add button on home screen
2. Choose the GPX file to import
3. Select the dives to import
4. Click accept

Changes made:
1. Added import option on add button in HomeActivity.java
2. Made DiveLocationLog class parcelable to be able to send
   and receive via intents.
3. Changed AndroidManifest.xml to include parent in activities
4. Renamed for more meaningful names
      PickLocation.java to PickLocationMap.java
      dive_loc_menu.xml to picklocation_map.xml
5. Rearranged added strings to appropriate groups
6. The strings included in languages German, French, Russian and
   Romanian are erased as they may not be conveying real meaning
7. Added up navigation to PickLocationMap activity
8. Changed title of PickLocationMap activity

Reported-by: marcmerlin
Signed-off-by: Venkatesh Shukla <venkatesh.shukla.eee11 at iitbhu.ac.in>
---
 .classpath                                    |   22 ++--
 AndroidManifest.xml                           |   35 +++++-
 res/drawable-hdpi/ic_action_accept.png        |  Bin 0 -> 401 bytes
 res/drawable-mdpi/ic_action_accept.png        |  Bin 0 -> 246 bytes
 res/drawable-xhdpi/ic_action_accept.png       |  Bin 0 -> 475 bytes
 res/layout/gpx_divelistitem.xml               |   69 +++++++++++
 res/layout/gpx_listitem.xml                   |   48 ++++++++
 res/menu/dive_loc_menu.xml                    |    9 --
 res/menu/dives.xml                            |    3 +
 res/menu/picklocation_gpx.xml                 |   11 ++
 res/menu/picklocation_map.xml                 |    9 ++
 res/values-de/strings.xml                     |    7 +-
 res/values-fr/strings.xml                     |    7 +-
 res/values-ro/strings.xml                     |    7 +-
 res/values-ru/strings.xml                     |    7 +-
 res/values/strings.xml                        |   26 ++++-
 src/org/subsurface/GpxParser.java             |  142
+++++++++++++++++++++++
 src/org/subsurface/HomeActivity.java          |  105 +++++++++--------
 src/org/subsurface/PickGpx.java               |  151
+++++++++++++++++++++++++
 src/org/subsurface/PickLocation.java          |   83 --------------
 src/org/subsurface/PickLocationGpx.java       |  123 ++++++++++++++++++++
 src/org/subsurface/PickLocationMap.java       |   77 +++++++++++++
 src/org/subsurface/model/DiveLocationLog.java |   37 +++++-
 src/org/subsurface/model/GpxFileInfo.java     |   70 ++++++++++++
 src/org/subsurface/ui/GpxDiveListAdapter.java |  108 ++++++++++++++++++
 src/org/subsurface/ui/GpxListAdapter.java     |   68 +++++++++++
 26 files changed, 1036 insertions(+), 188 deletions(-)
 create mode 100644 res/drawable-hdpi/ic_action_accept.png
 create mode 100644 res/drawable-mdpi/ic_action_accept.png
 create mode 100644 res/drawable-xhdpi/ic_action_accept.png
 create mode 100644 res/layout/gpx_divelistitem.xml
 create mode 100644 res/layout/gpx_listitem.xml
 delete mode 100644 res/menu/dive_loc_menu.xml
 create mode 100644 res/menu/picklocation_gpx.xml
 create mode 100644 res/menu/picklocation_map.xml
 create mode 100644 src/org/subsurface/GpxParser.java
 create mode 100644 src/org/subsurface/PickGpx.java
 delete mode 100644 src/org/subsurface/PickLocation.java
 create mode 100644 src/org/subsurface/PickLocationGpx.java
 create mode 100644 src/org/subsurface/PickLocationMap.java
 create mode 100644 src/org/subsurface/model/GpxFileInfo.java
 create mode 100644 src/org/subsurface/ui/GpxDiveListAdapter.java
 create mode 100644 src/org/subsurface/ui/GpxListAdapter.java

diff --git a/.classpath b/.classpath
index d63a936..ff65980 100644
--- a/.classpath
+++ b/.classpath
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con"
path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry exported="true" kind="lib"
path="lib/ormlite-android-4.43.jar"
sourcepath="C:/Users/aurelien/.m2/repository/com/j256/ormlite/ormlite-android/4.43/ormlite-android-4.43-sources.jar"/>
- <classpathentry exported="true" kind="lib"
path="lib/ormlite-core-4.43.jar"
sourcepath="C:/Users/aurelien/.m2/repository/com/j256/ormlite/ormlite-core/4.43/ormlite-core-4.43-sources.jar"/>
- <classpathentry exported="true" kind="con"
path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry exported="true" kind="con"
path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con"
path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con"
path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry exported="true" kind="con"
path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="lib"
path="libs/ormlite-android-4.48.jar"/>
+ <classpathentry exported="true" kind="lib"
path="libs/ormlite-core-4.48.jar"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cd72546..d38851a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,7 +6,7 @@
     <uses-sdk
         android:minSdkVersion="9"
         android:targetSdkVersion="19" />
-
+
     <permission
android:name="org.subsurface.mapfragment.permission.MAPS_RECEIVE"
android:protectionLevel="signature" />

     <uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -15,7 +15,7 @@
     <uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
-
+
     <uses-feature android:glEsVersion="0x00020000" android:required="true"
/>

     <application
@@ -31,9 +31,9 @@
         </activity>
         <activity android:name="SharePicturePositionActivity"
android:theme="@style/AppTheme">
             <intent-filter>
-             <action android:name="android.intent.action.SEND" />
+ <action android:name="android.intent.action.SEND" />
                 <category android:name="android.intent.category.DEFAULT" />
-             <data android:mimeType="image/jpeg" />
+ <data android:mimeType="image/jpeg" />
             </intent-filter>
         </activity>
         <service android:name="BackgroundLocationService"
android:process=":location_service"/>
@@ -43,8 +43,31 @@
         <activity android:name="DiveDetailActivity"
android:theme="@style/AppTheme.SubScreen" />
         <activity android:name="Preferences"
android:theme="@style/AppTheme" android:label="@string/menu_settings"
android:excludeFromRecents="true" />
         <activity android:name="MapActivity"
android:theme="@style/AppTheme.SubScreen"></activity>
-        <activity android:name="PickLocation"
android:theme="@style/AppTheme.SubScreen"></activity>
-
+        <activity
+            android:name="PickGpx"
+            android:theme="@style/AppTheme.SubScreen"
+            android:parentActivityName="HomeActivity">
+            <meta-data
+            android:name="android.support.PARENT_ACTIVITY"
+            android:value="org.subsurface.HomeActivity" />
+        </activity>
+        <activity
+            android:name="PickLocationMap"
+            android:theme="@style/AppTheme.SubScreen"
+            android:parentActivityName="HomeActivity">
+            <meta-data
+            android:name="android.support.PARENT_ACTIVITY"
+            android:value="org.subsurface.HomeActivity" />
+        </activity>
+        <activity
+            android:name="PickLocationGpx"
+            android:theme="@style/AppTheme.SubScreen"
+            android:parentActivityName="PickGpx">
+            <meta-data
+            android:name="android.support.PARENT_ACTIVITY"
+            android:value="org.subsurface.PickGpx" />
+        </activity>
+
         <meta-data
             android:name="com.google.android.maps.v2.API_KEY"
             android:value="AIzaSyDT6q4u6abJkH_VOFWqAS8MV3MhXsaQS3E" />
diff --git a/res/drawable-hdpi/ic_action_accept.png
b/res/drawable-hdpi/ic_action_accept.png
new file mode 100644
index
0000000000000000000000000000000000000000..700fc81518fcf6b50df4f4813efd0578c420279c
GIT binary patch
literal 401
zcmeAS at N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O`
z&)mfH)S%SFl*+=BsWuD at jK-cWjv*Dd-b}yG+hicnuK95)V at yNtjKi!o;_L+sVg-yf
z<xJuQj6n7tZ?B{+Q~u?9)=iJmQ&jwGck5DiY_3ffhXVtXKm!Ad0z>2f8Ov5KmD!o+
z=itOYrFxxJ=dOewZg!7u<vKYqo-Y)3is=$psAzj9`l0q?wZoy$)AMs5bPIfFJg at hF
zdw!?_OO07$z=uc{r$g%E9K8Fr9GJ_deBWB9BEBcA;o-_TPYxRW at Ho=6J<{o%km19v
zOeP0Bp1x5Ql&&jft5FF$Ag!w?-Wl at YSoHz7B*uS&%=cc&7)*?4s1K6s-XZ*2#pDBf
z)q7cQ2aA8#pBpFfvDGYXdw#<*^0Lnam#?Z9|J+$J=|J^Yr>>_{WbW;r8SJ6b%6~Jp
s=yLf~Y01Dpy*yA4GIE^w!C}C_#CG_t@}%d+z%XX;boFyt=akR{0HbJ|djJ3c

literal 0
HcmV?d00001

diff --git a/res/drawable-mdpi/ic_action_accept.png
b/res/drawable-mdpi/ic_action_accept.png
new file mode 100644
index
0000000000000000000000000000000000000000..41107b870a6cdf2fa983574294361cb545ad68ce
GIT binary patch
literal 246
zcmeAS at N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O`
z&)mfH)S%SFl*+=BsWw1GGdx`!Ln>}1B}lL?P7vu~`SAaLeVl at yw8!b`j2|Tx@*Jk8
zFWUOlAxlkQwPWi!zA5J&Z<Js86nNUxa0PqERkkC~9nKmrY<r`AOfZDMY{Jrj)0|dK
zX@!10${F(>e6VcFTPZtRc}1?JvSDAtwC4+o{u&F|yKCK+mXwq<zpUJ^+|KfsTT-WQ
s!TP5eeG(F<-4AL=ux?>uU}R%>+m>-ZX1 at 3)pc@%HUHx3vIVCg!05Z{8<NyEw

literal 0
HcmV?d00001

diff --git a/res/drawable-xhdpi/ic_action_accept.png
b/res/drawable-xhdpi/ic_action_accept.png
new file mode 100644
index
0000000000000000000000000000000000000000..6ee32b64df5f43346d78bf38a30c6194ae37e6a6
GIT binary patch
literal 475
zcmeAS at N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*9U+7;8OU978H at y_t1Tu*pEcwX>Z20 at IqqOj{Uq9k at 3ze9U9M
z)lmL*9cu(5Z$ty at gS0m0_72Y*2ipFaZ*FFOJmGt<v)Qavpj}|#@MTJ>rEg at N+t&rB
zf~VDorivcQ(D+nh5!jo(T)d>t_y<GXiEOS_r}uwf&3HP0YS8?HYzGwOa~Wbj1+pE`
zl#gY&^C^&3z{zeFgVRZOK?je at kBlwT#AO}CKP53MRO;56p4h+gTFfWEy$y0ASNAAa
zURGbfw0zE|Ym8UeN|#(e&G#got<v at __q%GX^Nz9^2HS5u%}qaW%3y`<bDtO9_p}&x
z{hY~Hepq#yz^^IgRa_y_KHP^@->-MR|LI)6<2eD96V+>(Eu1DOz27U4CETR)GfdlY
zzQB@`+vB))w<rY8TPnVR({YtewDA=YrmMlLY_iR72sFKWpeFS6=Iw`V0xqZ6Z}R^T
zZqoRfAgyHYB%m%|m8`t}Z^+*j+jd-9#HHx_Gm7Kozf at LeSTzLfW8z`hR%QAuE&KEf
Okf5ilpUXO at geCx?AHK{0

literal 0
HcmV?d00001

diff --git a/res/layout/gpx_divelistitem.xml
b/res/layout/gpx_divelistitem.xml
new file mode 100644
index 0000000..32af1ae
--- /dev/null
+++ b/res/layout/gpx_divelistitem.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="10dp" >
+    <CheckBox
+        android:id="@+id/cbGpxDive"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_centerVertical="true"
+        android:focusable="false"
+        android:padding="2dp" />
+
+    <TextView
+        android:id="@+id/tvGpxDiveDate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentRight="true"
+        android:gravity="right"
+        android:textAlignment="gravity"
+        android:textSize="12sp"
+        android:text="Dive Date" />
+
+    <TextView
+        android:id="@+id/tvGpxDiveTime"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_below="@id/tvGpxDiveDate"
+        android:gravity="right"
+        android:textAlignment="gravity"
+        android:textSize="12sp"
+        android:text="Dive time" />
+
+    <TextView
+        android:id="@+id/tvGpxDiveName"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/cbGpxDive"
+        android:layout_toLeftOf="@id/tvGpxDiveDate"
+        android:layout_alignBottom="@id/tvGpxDiveTime"
+        android:layout_alignTop="@id/tvGpxDiveDate"
+        android:layout_centerVertical="true"
+        android:ellipsize="middle"
+        android:singleLine="true"
+        android:paddingLeft="15dp"
+        android:gravity="center_vertical|left"
+        android:textAlignment="gravity"
+        android:textSize="17sp"
+        android:text="Dive Name"
+        />
+
+    <TextView
+        android:id="@+id/tvGpxDiveLoc"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentBottom="true"
+        android:layout_toRightOf="@id/cbGpxDive"
+        android:layout_below="@id/tvGpxDiveTime"
+        android:gravity="center_vertical|right"
+        android:paddingTop="3dp"
+        android:textAlignment="gravity"
+        android:textSize="12sp"
+        android:text="(Latitude, Longitude)" />
+
+</RelativeLayout>
diff --git a/res/layout/gpx_listitem.xml b/res/layout/gpx_listitem.xml
new file mode 100644
index 0000000..386c144
--- /dev/null
+++ b/res/layout/gpx_listitem.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="10dp">
+
+    <TextView
+        android:id="@+id/tvGpxName"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:layout_centerVertical="true"
+        android:text="@string/gpx_dummy_name"
+        android:textSize="17sp" />
+
+    <TextView
+        android:id="@+id/tvGpxDate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_toRightOf="@id/tvGpxName"
+        android:padding="2dp"
+        android:text="@string/gpx_dummy_date"
+        android:gravity="center_vertical|right"
+        android:textSize="12sp" />
+
+    <TextView
+        android:id="@+id/tvGpxDirectory"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentBottom="true"
+        android:layout_toRightOf="@id/tvGpxName"
+        android:layout_below="@id/tvGpxDate"
+        android:ellipsize="start"
+        android:padding="2dp"
+        android:singleLine="true"
+        android:text="@string/gpx_dummy_path"
+        android:gravity="center_vertical|right"
+        android:textSize="12sp" />
+
+</RelativeLayout>
+
diff --git a/res/menu/dive_loc_menu.xml b/res/menu/dive_loc_menu.xml
deleted file mode 100644
index da50d04..0000000
--- a/res/menu/dive_loc_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item
-        android:id="@+id/dive_loc_finish"
-        android:title="@string/menu_save"
-        android:icon="@drawable/ic_menu_send_now"
-        android:showAsAction="always"
-        />
-</menu>
diff --git a/res/menu/dives.xml b/res/menu/dives.xml
index 0e47fa8..593f0ea 100644
--- a/res/menu/dives.xml
+++ b/res/menu/dives.xml
@@ -23,6 +23,9 @@
             <item
                 android:id="@+id/menu_new_map"
                 android:title="@string/menu_new_map"/>
+            <item
+                android:id="@+id/menu_new_import"
+                android:title="@string/menu_new_gpx"/>
         </menu>
     </item>
     <item
diff --git a/res/menu/picklocation_gpx.xml b/res/menu/picklocation_gpx.xml
new file mode 100644
index 0000000..7868bcd
--- /dev/null
+++ b/res/menu/picklocation_gpx.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/gpxmenu_send"
+        android:icon="@drawable/ic_action_accept"
+        android:showAsAction="always"
+        android:title="@string/menu_accept"/>
+
+</menu>
+
diff --git a/res/menu/picklocation_map.xml b/res/menu/picklocation_map.xml
new file mode 100644
index 0000000..55d839d
--- /dev/null
+++ b/res/menu/picklocation_map.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/dive_loc_finish"
+        android:title="@string/menu_accept"
+        android:icon="@drawable/ic_action_accept"
+        android:showAsAction="always"
+        />
+</menu>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c12a10b..fcfb9e9 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -120,10 +120,5 @@
     <string name="settings_other_website_summary">Subsurface Webseite
anzeigen</string>
     <string name="settings_other_version">Version</string>
     <string name="settings_other_version_summary">Aktuelle Version:
%s</string>
-
-    <string name="menu_new_current">Strom</string>
-    <string name="menu_new_map">Verwenden Karte</string>
-    <string name="instruction_pickloc">Wählen Sie das Tauchpunkt</string>
-    <string name="hint_dive_offset">Minuten nach dem letzten
Tauchgang</string>
-    <string name="confirmation_dive_picked_sent">Dive %s hinzugefügt wurde
und gesendet</string>
 </resources>
+
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 07ce5cc..d8727d1 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -120,10 +120,5 @@
     <string name="settings_other_website_summary">Ouvrir la page du
projet</string>
     <string name="settings_other_version">Version</string>
     <string name="settings_other_version_summary">Version actuelle :
%s</string>
-
-    <string name="menu_new_current">courant</string>
-    <string name="menu_new_map">Utilisez la carte</string>
-    <string name="instruction_pickloc">Sélectionnez le point de
plongée</string>
-    <string name="hint_dive_offset">Minutes passé après la plongée</string>
-    <string name="confirmation_dive_picked_sent">Dive %s a été ajouté et
envoyé</string>
 </resources>
+
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index dcb1166..53e1a7c 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -120,10 +120,5 @@
     <string name="settings_other_website_summary">Deschide pagina
proiectului</string>
     <string name="settings_other_version">Versiune</string>
     <string name="settings_other_version_summary">Versiune actuală:
%s</string>
-
-    <string name="menu_new_current">curent</string>
-    <string name="menu_new_map">Folosiți harta</string>
-    <string name="instruction_pickloc">Selectați punctul de
scufundare</string>
-    <string name="hint_dive_offset">Minute trecut după scufundare</string>
-    <string name="confirmation_dive_picked_sent">Dive %s a fost adăugat și
a trimis</string>
 </resources>
+
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index f47eed2..df9db02 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -121,10 +121,5 @@
     <string name="settings_other_website_summary">Открыть сайт
проекта</string>
     <string name="settings_other_version">Версия</string>
     <string name="settings_other_version_summary">Текущая версия:
%s</string>
-
-    <string name="menu_new_current">ток</string>
-    <string name="menu_new_map">Используйте карту</string>
-    <string name="instruction_pickloc">Выберите точку погружения</string>
-    <string name="hint_dive_offset">Протокол мимо после погружения</string>
-    <string name="confirmation_dive_picked_sent">Была добавлена ​​Dive %s
и послал</string>
 </resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index da661b4..2226c45 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -39,6 +39,10 @@
     <string name="menu_edit">Edit</string>
     <string name="menu_start_background_service">Start service</string>
     <string name="menu_stop_background_service">Stop service</string>
+    <string name="menu_new_current">Current</string>
+    <string name="menu_new_map">Use map</string>
+    <string name="menu_new_gpx">Import local GPX file</string>
+    <string name="menu_accept">Accept</string>

     <string name="notification_background_service_on">Background location
service active</string>

@@ -51,6 +55,7 @@
     <string name="confirmation_location_picked">Location %s has been
added</string>
     <string name="confirmation_location_sent">Location "%s" has been
sent</string>
     <string name="confirmation_locations_sent">%1$d/%2$d locations have
been sent</string>
+    <string name="confirmation_dive_picked_sent">Dive %s has been added
and sent</string>

     <string name="error_location">Could not receive location</string>
     <string name="error_send">Could not send location</string>
@@ -71,7 +76,11 @@
     <string name="error_open_image">Could not open image</string>
     <string name="error_delete_dives">Could not delete dives</string>
     <string name="error_delete_dive">Could not delete dive</string>
-
+
+    <string name="error_sd_unavailable">SD Card Error</string>
+    <string name="error_no_dive_found">No dive info recieved</string>
+    <string name="error_gpx_no_file_found">No GPX file found in the SD
card</string>
+    <string name="error_gpx_no_dive_found">No Divepoints found in the GPX
file</string>

     <string name="account_link_create">Create a new account</string>
     <string name="account_link_retrieve">Retrieve an account</string>
@@ -94,6 +103,7 @@
     <string name="hint_email">Email address</string>
     <string name="hint_id">ID</string>
     <string name="hint_dive_name">Dive name</string>
+    <string name="hint_dive_offset">Minutes past after dive</string>

     <string name="settings_category_ws">Server and account</string>
     <string name="settings_destination_url">Web-service URL</string>
@@ -123,9 +133,15 @@
     <string name="settings_other_version">Version</string>
     <string name="settings_other_version_summary">Current version:
%s</string>

-    <string name="menu_new_current">Current</string>
-    <string name="menu_new_map">Use map</string>
     <string name="instruction_pickloc">Select the dive point</string>
-    <string name="hint_dive_offset">Minutes past after dive</string>
-    <string name="confirmation_dive_picked_sent">Dive %s has been added
and sent</string>
+
+    <string name="gpx_dummy_name">Name of GPX file</string>
+    <string name="gpx_dummy_path">Path to GPX file :
qwertyuiopasdfghjklzxcvbnm</string>
+    <string name="gpx_dummy_date">yyyy-MMM-dd KK:mm:ss a</string>
+    <string name="gpx_dialog_search">Searching for GPX files</string>
+
+    <string name="title_picklocationgpx">Select Locations</string>
+    <string name="title_pickgpx">Select GPX file</string>
+    <string name="title_picklocationmap">Select Location</string>
+    <string name="title_map_marker">Dive Spot</string>
 </resources>
diff --git a/src/org/subsurface/GpxParser.java
b/src/org/subsurface/GpxParser.java
new file mode 100644
index 0000000..616a7b2
--- /dev/null
+++ b/src/org/subsurface/GpxParser.java
@@ -0,0 +1,142 @@
+package org.subsurface;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.subsurface.model.DiveLocationLog;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.location.Location;
+import android.util.Xml;
+
+public class GpxParser {
+
+ private static final String ns = null;
+ SimpleDateFormat isotime;
+
+ public GpxParser() {
+ super();
+ isotime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",
+ Locale.getDefault());
+ }
+
+ public List<DiveLocationLog> parse(InputStream in) throws
XmlPullParserException, IOException, ParseException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(in, null);
+ parser.nextTag();
+ return readFeed(parser);
+ } finally {
+ in.close();
+ }
+ }
+
+ private List<DiveLocationLog> readFeed(XmlPullParser parser) throws
XmlPullParserException, IOException, ParseException {
+ List<DiveLocationLog> alldivelogs = new ArrayList<DiveLocationLog>();
+ parser.require(XmlPullParser.START_TAG, ns, "gpx");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ // Only takes care of waypoints in the gpx
+ if (name.equals("wpt")) {
+ Location diveloc = readLocation(parser);
+ DiveLocationLog dll = readEntry(parser);
+ dll.setLocation(diveloc);
+ alldivelogs.add(dll);
+ } else {
+ skip(parser);
+ }
+ }
+ return alldivelogs;
+ }
+
+ // Parses the contents of an entry. If it encounters a name or time,
hands them off
+ // to their respective "read" methods for processing. Otherwise, skips
the tag.
+ private DiveLocationLog readEntry(XmlPullParser parser) throws
XmlPullParserException, IOException, ParseException {
+ parser.require(XmlPullParser.START_TAG, ns, "wpt");
+ String divename = null;
+ Location diveloc = new Location("");
+ long divetimestamp = -1;
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ if (name.equals("name")) {
+ divename = readName(parser);
+ } else if (name.equals("time")) {
+ divetimestamp = readTime(parser);
+ } else {
+ skip(parser);
+ }
+ }
+ return new DiveLocationLog(diveloc, divename, divetimestamp);
+ }
+
+ // Processes title tags in the feed.
+ private String readName(XmlPullParser parser) throws IOException,
XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, ns, "name");
+ String title = readText(parser);
+ parser.require(XmlPullParser.END_TAG, ns, "name");
+ return title;
+ }
+
+ // Extracts latitude and longitude attributr from wpt tag in the feed.
+ private Location readLocation(XmlPullParser parser) throws IOException,
XmlPullParserException {
+ Location diveloc = new Location("");
+ Double divelat, divelon;
+ parser.require(XmlPullParser.START_TAG, ns, "wpt");
+ divelat = Double.valueOf(parser.getAttributeValue(null, "lat"));
+ divelon = Double.valueOf(parser.getAttributeValue(null, "lon"));
+ diveloc.setLatitude(divelat);
+ diveloc.setLongitude(divelon);
+ return diveloc;
+ }
+
+ // Extracts time from the GPX given in ISO-8601 format and converts to
UTC timestamp
+ private long readTime(XmlPullParser parser) throws IOException,
XmlPullParserException, ParseException {
+ parser.require(XmlPullParser.START_TAG, ns, "time");
+ String time = readText(parser);
+ long timestamp = 0;
+ timestamp = isotime.parse(time).getTime();
+ parser.require(XmlPullParser.END_TAG, ns, "time");
+ return timestamp;
+ }
+
+ // For the tags name and time extracts their text values.
+ private String readText(XmlPullParser parser) throws IOException,
XmlPullParserException {
+ String result = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+ return result;
+ }
+
+ // Skips the tag names not required
+ private void skip(XmlPullParser parser) throws XmlPullParserException,
IOException {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ throw new IllegalStateException();
+ }
+ int depth = 1;
+ while (depth != 0) {
+ switch (parser.next()) {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/org/subsurface/HomeActivity.java
b/src/org/subsurface/HomeActivity.java
index ee8ed1b..f8ba3ea 100644
--- a/src/org/subsurface/HomeActivity.java
+++ b/src/org/subsurface/HomeActivity.java
@@ -21,7 +21,6 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.res.Resources;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -139,7 +138,7 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  @Override
  protected void onPostExecute(Integer success) {
  ((DiveArrayAdapter) getListAdapter()).notifyDataSetChanged();
- Toast.makeText(HomeActivity.this, success,Toast.LENGTH_SHORT).show();
+ Toast.makeText(HomeActivity.this, success, Toast.LENGTH_SHORT).show();
  refreshItem.setActionView(null);
  }
  }.execute();
@@ -201,7 +200,7 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  runOnUiThread(new Runnable() {
  public void run() {
  ((DiveArrayAdapter) getListAdapter()).notifyDataSetChanged();
- Toast.makeText(HomeActivity.this,
getString(R.string.confirmation_locations_sent, successCount,
totalCount),Toast.LENGTH_SHORT).show();
+ Toast.makeText(HomeActivity.this,
getString(R.string.confirmation_locations_sent, successCount, totalCount),
Toast.LENGTH_SHORT).show();
  }
  });
  }
@@ -294,8 +293,7 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  public void onLocationChanged(final Location location) {
  if (!cancel.get()) {
  waitDialog.dismiss();
- Toast.makeText(HomeActivity.this,
getString(R.string.confirmation_location_picked,
- locationLog.getName()), Toast.LENGTH_SHORT).show();
+ Toast.makeText(HomeActivity.this,
getString(R.string.confirmation_location_picked, locationLog.getName()),
Toast.LENGTH_SHORT).show();
  new Thread(new Runnable() {
  public void run() {
  locationLog.setLocation(location);
@@ -428,51 +426,55 @@ public class HomeActivity extends
SherlockListActivity implements com.actionbars
  startActivity(detailIntent);
  }

-
- private static final int pickreq_code = 999;
- static int lat;
- static int lon;
+ private static final int pick_map_reqcode = 999;
+ private static final int pick_gpxfile_reqcode = 998;
  //Receive result from map activity for map picker event
  public void onActivityResult(int requestcode, int resultCode, Intent
data) {
- if (pickreq_code == requestcode && resultCode == RESULT_OK && data !=
null) {
+ if(resultCode == RESULT_OK && data != null) {
  Bundle rec_bundle = data.getExtras();
- final LatLng loc = (LatLng) rec_bundle.get("location");
- if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
-
- LinearLayout linlay = new LinearLayout(this);
- linlay.setOrientation(LinearLayout.VERTICAL);
- final EditText divename = new EditText(this);
- divename.setHint(R.string.hint_dive_name);
- divename.setInputType(InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- final EditText diveoff = new EditText(this);
- diveoff.setHint(R.string.hint_dive_offset);
- diveoff.setInputType(InputType.TYPE_CLASS_NUMBER);
- linlay.addView(divename);
- linlay.addView(diveoff);
-
- builder.setView(linlay);
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setTitle(getString(R.string.dialog_location_name))
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- long minuteoff;
- String mins = diveoff.getText().toString();
- if(mins.contentEquals(""))
- minuteoff = 0;
- else
- minuteoff = Integer.valueOf(mins);
- // Dateutils.getFakeUtcDate returns timestamp in milliseconds. To include
offset multiply by 60000
- sendMapDiveLog(divename.getText().toString(), loc,
DateUtils.getFakeUtcDate() - minuteoff * 60000);
- }
- }).create().show();
- } else {
- showGpsWarning();
+ switch(requestcode) {
+ case pick_map_reqcode:
+ final LatLng loc = (LatLng) rec_bundle.get("location");
+ if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ LinearLayout linlay = new LinearLayout(this);
+ linlay.setOrientation(LinearLayout.VERTICAL);
+ final EditText divename = new EditText(this);
+ divename.setHint(R.string.hint_dive_name);
+ divename.setInputType(InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+ final EditText diveoff = new EditText(this);
+ diveoff.setHint(R.string.hint_dive_offset);
+ diveoff.setInputType(InputType.TYPE_CLASS_NUMBER);
+ linlay.addView(divename);
+ linlay.addView(diveoff);
+ builder.setView(linlay);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setTitle(getString(R.string.dialog_location_name))
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ long minuteoff;
+ String mins = diveoff.getText().toString();
+ if(mins.contentEquals(""))
+ minuteoff = 0;
+ else
+ minuteoff = Integer.valueOf(mins);
+ // Dateutils.getFakeUtcDate returns timestamp in milliseconds. To include
offset multiply by 60000
+ sendMapDiveLog(divename.getText().toString(), loc,
DateUtils.getFakeUtcDate() - minuteoff * 60000);
+ }
+ }).create().show();
+ } else {
+ showGpsWarning();
+ }
+ return;
+ case pick_gpxfile_reqcode:
+ List<DiveLocationLog> gpxdivelogs = (ArrayList<DiveLocationLog>)
rec_bundle.get("gpxdivelog");
+ sendDives(gpxdivelogs);
+ return;
  }
- } else {
- Toast.makeText(this, "No location detected", Toast.LENGTH_SHORT).show();
+ } else { // either some error has occurred or no data has been received
+ Toast.makeText(this, R.string.error_no_dive_found,
Toast.LENGTH_SHORT).show();
  }
  }

@@ -485,8 +487,8 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  return true;
  case R.id.menu_new_map:
  //Pick a location from map
- Intent picklocation = new Intent(this, PickLocation.class);
- startActivityForResult(picklocation, pickreq_code);
+ Intent picklocation = new Intent(this, PickLocationMap.class);
+ startActivityForResult(picklocation, pick_map_reqcode);
  return true;
  case R.id.menu_new_current:
  // Locate has been clicked
@@ -509,6 +511,11 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  showGpsWarning();
  }
  return true;
+ case R.id.menu_new_import:
+ // Pick a GPX file from sd card
+ Intent pickGpx = new Intent(this, PickGpx.class);
+ startActivityForResult(pickGpx, pick_gpxfile_reqcode);
+ return true;
  case R.id.menu_send_all:
  // Send has been clicked
  sendDives(DiveController.instance.getPendingLogs());
@@ -633,7 +640,7 @@ public class HomeActivity extends SherlockListActivity
implements com.actionbars
  break;
  }
  actionMode.finish();
- return true;
+ return true;
  }

  @Override
diff --git a/src/org/subsurface/PickGpx.java
b/src/org/subsurface/PickGpx.java
new file mode 100644
index 0000000..f424e3f
--- /dev/null
+++ b/src/org/subsurface/PickGpx.java
@@ -0,0 +1,151 @@
+package org.subsurface;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import org.subsurface.model.DiveLocationLog;
+import org.subsurface.model.GpxFileInfo;
+import org.subsurface.ui.GpxListAdapter;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.NavUtils;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockListActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+
+public class PickGpx extends SherlockListActivity implements
OnItemClickListener
+{
+ ArrayList<GpxFileInfo> allgpxfiles;
+ File sd_card;
+ GpxListAdapter gla;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ getSupportActionBar();
+ getSherlock().setProgressBarIndeterminateVisibility(true);
+ getSupportActionBar().setTitle(R.string.title_pickgpx);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ allgpxfiles = new ArrayList<GpxFileInfo>();
+ createAdapter();
+ ListView listview = getListView();
+ listview.setOnItemClickListener(this);
+ }
+
+ private void createAdapter() {
+ String sd_state = Environment.getExternalStorageState();
+ if(sd_state.contentEquals(Environment.MEDIA_MOUNTED) ||
sd_state.contentEquals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
+ sd_card = Environment.getExternalStorageDirectory();
+ GetAllGpx getallgpx = new GetAllGpx();
+ getallgpx.execute(sd_card);
+ } else {
+ //SD card is unavailable, or unreadable
+ Toast.makeText(this, R.string.error_sd_unavailable,
Toast.LENGTH_SHORT).show();
+ Intent resultIntent = new Intent();
+ setResult(Activity.RESULT_CANCELED, resultIntent);
+ finish();
+ }
+ }
+
+ //Recursively get all the GPX files in the SD card
+ private ArrayList<GpxFileInfo> getGpxInDir(File file) {
+ ArrayList<GpxFileInfo> gpxfiles = new ArrayList<GpxFileInfo>();
+ File[] filelist =  file.listFiles();
+ for(File f: filelist) {
+ if(!f.getName().startsWith(".")) {
+ // Leave out hidden directories
+ if(f.isDirectory()) {
+ gpxfiles.addAll(getGpxInDir(f));
+ }
+ else if(f.getName().toLowerCase(Locale.getDefault()).endsWith(".gpx")) {
+ String name = f.getName();
+ String path = f.getPath();
+ String directory = f.getParent()
+ .replace(sd_card.getPath(), "~/")
+ .replace("//", "/");
+ long timestamp = f.lastModified();
+ gpxfiles.add(new GpxFileInfo(name, path, timestamp, directory));
+ }
+ }
+ }
+ return gpxfiles;
+ }
+
+ private class GetAllGpx extends AsyncTask<File, Void, Void> {
+
+ @Override
+ protected Void doInBackground(File... allfiles) {
+ //TODO find a way to add items as they are found in the list using
notifyDataSetChange
+ for(File file: allfiles) {
+ allgpxfiles = getGpxInDir(file);
+ }
+ return null;
+    }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if(allgpxfiles.size() == 0) {
+ // No GPX files found in the SD card
+ Toast.makeText(PickGpx.this, getString(R.string.error_gpx_no_file_found),
Toast.LENGTH_SHORT).show();
+ Intent resultIntent = new Intent();
+ setResult(Activity.RESULT_CANCELED, resultIntent);
+ finish();
+ } else {
+ // Found some gpx files
+ getSherlock().setProgressBarIndeterminateVisibility(false);
+ gla = new GpxListAdapter(PickGpx.this, allgpxfiles);
+ setListAdapter(gla);
+ }
+ super.onPostExecute(result);
+ }
+ }
+
+ private static final int pick_gpxlocation_reqcode = 997;
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
+ Intent parseGpxIntent = new Intent(this,PickLocationGpx.class);
+ parseGpxIntent.putExtra("gpx_filepath",
allgpxfiles.get(position).getPath());
+ startActivityForResult(parseGpxIntent, pick_gpxlocation_reqcode);
+ }
+
+ public void onActivityResult(int requestcode, int resultCode, Intent
data) {
+ if(resultCode == RESULT_OK && data != null) {
+ Bundle rec_bundle = data.getExtras();
+ switch(requestcode) {
+ case pick_gpxlocation_reqcode:
+ @SuppressWarnings("unchecked")
+ ArrayList<DiveLocationLog> gpxdivelogs = (ArrayList<DiveLocationLog>)
rec_bundle.get("gpxdivelog");
+ Intent resultIntent = new Intent();
+ Bundle diveBundle = new Bundle();
+ diveBundle.putParcelableArrayList("gpxdivelog", gpxdivelogs);
+ resultIntent.putExtras(diveBundle);
+ setResult(Activity.RESULT_OK, resultIntent);
+ finish();
+ return;
+ }
+ } else { // either some error has occurred or no data has been received
+ Toast.makeText(this, R.string.error_no_dive_found,
Toast.LENGTH_SHORT).show();
+ }
+ }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/org/subsurface/PickLocation.java
b/src/org/subsurface/PickLocation.java
deleted file mode 100644
index fb4c7b7..0000000
--- a/src/org/subsurface/PickLocation.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.subsurface;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-import com.google.android.gms.maps.GoogleMap;
-import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
-import com.google.android.gms.maps.SupportMapFragment;
-import com.google.android.gms.maps.model.LatLng;
-import com.google.android.gms.maps.model.MarkerOptions;
-
-public class PickLocation extends SherlockFragmentActivity implements
OnMapClickListener
-{
-
-    private GoogleMap mMap;
-    public static LatLng latlng;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.dive_picklocation);
-        setUpMapIfNeeded();
-    }
-
-    @Override
- public boolean onCreateOptionsMenu(Menu menu)
-    {
-     getSupportMenuInflater().inflate(R.menu.dive_loc_menu, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item)
- {
- switch(item.getItemId())
- {
- case R.id.dive_loc_finish:
- Intent resultIntent = new Intent();
- resultIntent.putExtra("location", latlng);
- setResult(Activity.RESULT_OK, resultIntent);
- finish();
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
-    protected void onResume() {
-        super.onResume();
-        setUpMapIfNeeded();
-    }
-
-    private void setUpMapIfNeeded()
-    {
-        if (mMap == null)
-        {
-            mMap = ((SupportMapFragment)
getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
-            if (mMap != null)
-            {
-                setUpMap();
-            }
-        }
-    }
-
-    private void setUpMap()
-    {
-        mMap.setOnMapClickListener(this);
-    }
-
-   @Override
- public void onMapClick(LatLng loc)
- {
- mMap.clear();
- mMap.addMarker(new MarkerOptions().position(loc).title("Dive Spot"));
- latlng = loc;
- }
-
-}
diff --git a/src/org/subsurface/PickLocationGpx.java
b/src/org/subsurface/PickLocationGpx.java
new file mode 100644
index 0000000..c1bb6fa
--- /dev/null
+++ b/src/org/subsurface/PickLocationGpx.java
@@ -0,0 +1,123 @@
+package org.subsurface;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+
+import org.subsurface.model.DiveLocationLog;
+import org.subsurface.ui.GpxDiveListAdapter;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.NavUtils;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockListActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+
+public class PickLocationGpx extends SherlockListActivity implements
OnItemClickListener {
+
+ ListView listview;
+ ArrayList<DiveLocationLog> allgpxlogs;
+ Intent resultIntent;
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ allgpxlogs = new ArrayList<DiveLocationLog>();
+ resultIntent = new Intent();
+ String gpx_filepath = getIntent().getStringExtra("gpx_filepath");
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ getSupportActionBar().setTitle(R.string.title_picklocationgpx);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSherlock().setProgressBarIndeterminateVisibility(true);
+ GetDivesFromGpx gdfg = new GetDivesFromGpx();
+ gdfg.execute(gpx_filepath);
+ listview = getListView();
+ listview.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long
id) {
+ CheckBox cbDive = (CheckBox) v.findViewById(R.id.cbGpxDive);
+ cbDive.setChecked(!cbDive.isChecked());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getSupportMenuInflater().inflate(R.menu.picklocation_gpx, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ GpxDiveListAdapter gpxDla = (GpxDiveListAdapter) getListAdapter();
+ switch(item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ case R.id.gpxmenu_send:
+ Bundle diveBundle = new Bundle();
+ diveBundle.putParcelableArrayList("gpxdivelog",
gpxDla.getSelectedDives());
+ resultIntent.putExtras(diveBundle);
+ setResult(Activity.RESULT_OK, resultIntent);
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ // Async task which parses data of GPX file and sets the listAdapter
+ private class GetDivesFromGpx extends AsyncTask<String, Void, Void>{
+ FileInputStream xmlstream;
+ GpxParser parser;
+
+ @Override
+ protected Void doInBackground(String ... paths ) {
+ for(String path:paths) {
+ try
+ {
+ xmlstream = new FileInputStream(path);
+ parser = new GpxParser();
+ allgpxlogs.addAll(parser.parse(xmlstream));
+ }
+ catch (FileNotFoundException e)
+ {
+ e.printStackTrace();
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if(allgpxlogs.size() == 0) {
+ //No dive logs found in gpx. Exit
+ Toast.makeText(PickLocationGpx.this, R.string.error_gpx_no_dive_found,
Toast.LENGTH_SHORT).show();
+ setResult(Activity.RESULT_CANCELED, resultIntent);
+ finish();
+ }
+ GpxDiveListAdapter adapter = new GpxDiveListAdapter(PickLocationGpx.this,
allgpxlogs);
+ setListAdapter(adapter);
+ getSherlock().setProgressBarIndeterminateVisibility(false);
+ super.onPostExecute(result);
+ }
+ }
+}
diff --git a/src/org/subsurface/PickLocationMap.java
b/src/org/subsurface/PickLocationMap.java
new file mode 100644
index 0000000..3ae3ff1
--- /dev/null
+++ b/src/org/subsurface/PickLocationMap.java
@@ -0,0 +1,77 @@
+package org.subsurface;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.NavUtils;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+
+public class PickLocationMap extends SherlockFragmentActivity implements
OnMapClickListener {
+
+ private GoogleMap mMap;
+ public static LatLng latlng;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle(R.string.title_picklocationmap);
+ setContentView(R.layout.dive_picklocation);
+ setUpMapIfNeeded();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getSupportMenuInflater().inflate(R.menu.picklocation_map, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ case R.id.dive_loc_finish:
+ Intent resultIntent = new Intent();
+ resultIntent.putExtra("location", latlng);
+ setResult(Activity.RESULT_OK, resultIntent);
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setUpMapIfNeeded();
+ }
+
+ private void setUpMapIfNeeded() {
+ if (mMap == null) {
+ mMap = ((SupportMapFragment)
getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
+ if (mMap != null) {
+ setUpMap();
+ }
+ }
+ }
+
+ private void setUpMap() {
+ mMap.setOnMapClickListener(this);
+ }
+
+ @Override
+ public void onMapClick(LatLng loc) {
+ mMap.clear();
+ latlng = loc;
+ }
+
+}
diff --git a/src/org/subsurface/model/DiveLocationLog.java
b/src/org/subsurface/model/DiveLocationLog.java
index 48f078e..daa6347 100644
--- a/src/org/subsurface/model/DiveLocationLog.java
+++ b/src/org/subsurface/model/DiveLocationLog.java
@@ -21,6 +21,8 @@ import com.j256.ormlite.field.DatabaseField;
 import com.j256.ormlite.table.DatabaseTable;

 import android.location.Location;
+import android.os.Parcel;
+import android.os.Parcelable;

 /**
  * Model for GPS location.
@@ -28,7 +30,7 @@ import android.location.Location;
  *
  */
 @DatabaseTable(tableName = "dives")
-public class DiveLocationLog {
+public class DiveLocationLog implements Parcelable {

  public static final String KEY_ID = "_id";
  public static final String KEY_LATITUDE = "latitude";
@@ -155,4 +157,37 @@ public class DiveLocationLog {
  .append(longitude).append('/')
  .append(sent).toString();
  }
+
+ @Override
+ public int describeContents() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeDouble(this.latitude);
+ out.writeDouble(this.longitude);
+ out.writeString(this.name);
+ out.writeLong(timestamp);
+ }
+
+ public static final Parcelable.Creator<DiveLocationLog> CREATOR = new
Parcelable.Creator<DiveLocationLog>() {
+ public DiveLocationLog createFromParcel(Parcel in) {
+ return new DiveLocationLog(in);
+ }
+
+ public DiveLocationLog[] newArray(int size) {
+ return new DiveLocationLog[size];
+ }
+ };
+
+ private DiveLocationLog(Parcel in) {
+ this.latitude = in.readDouble();
+ this.longitude = in.readDouble();
+ this.name = in.readString();
+ this.timestamp = in.readLong();
+ this.sent = false;
+ }
+
 }
diff --git a/src/org/subsurface/model/GpxFileInfo.java
b/src/org/subsurface/model/GpxFileInfo.java
new file mode 100644
index 0000000..9606f43
--- /dev/null
+++ b/src/org/subsurface/model/GpxFileInfo.java
@@ -0,0 +1,70 @@
+package org.subsurface.model;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+public class GpxFileInfo
+{
+ private String name;
+ private String path;
+ private String date;
+ private String directory;
+ private final SimpleDateFormat sdf;
+
+ public GpxFileInfo(String name, String path, long timestamp, String
directory)
+ {
+ super();
+ this.name = name;
+ this.path = path;
+ sdf = new SimpleDateFormat("MM/dd/yy HH:mm:ss", Locale.getDefault());
+ this.date = sdf.format(timestamp);
+ this.directory = directory;
+
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ return;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public void setPath(String path)
+ {
+ this.path = path;
+ return;
+ }
+
+ public String getDate()
+ {
+ return date;
+ }
+
+ public void setDate(long timestamp)
+ {
+ this.date = sdf.format(timestamp);
+ return;
+ }
+
+
+ public String getDirectory()
+ {
+ return directory;
+ }
+
+ public void setDirectory(String directory)
+ {
+ this.directory = directory;
+ return;
+ }
+}
+
diff --git a/src/org/subsurface/ui/GpxDiveListAdapter.java
b/src/org/subsurface/ui/GpxDiveListAdapter.java
new file mode 100644
index 0000000..98654fb
--- /dev/null
+++ b/src/org/subsurface/ui/GpxDiveListAdapter.java
@@ -0,0 +1,108 @@
+package org.subsurface.ui;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import org.subsurface.R;
+import org.subsurface.model.DiveLocationLog;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.TextView;
+
+public class GpxDiveListAdapter extends BaseAdapter {
+
+ private final ArrayList<DiveLocationLog> gpxdivelogs;
+ private final LayoutInflater inflater;
+ private final SimpleDateFormat sdfdate;
+ private final SimpleDateFormat sdftime;
+ private final boolean[] cbState; // boolean array to save which
checkboxes are checked
+
+ public GpxDiveListAdapter(Context context, ArrayList<DiveLocationLog>
gpxdivelogs) {
+ this.gpxdivelogs = gpxdivelogs;
+ inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ sdfdate = new SimpleDateFormat("MM/dd/yyyy", Locale.getDefault());
+ sdftime = new SimpleDateFormat("HH:mm", Locale.getDefault());
+ cbState = new boolean[gpxdivelogs.size()];
+
+ }
+
+ public void addItem(final DiveLocationLog gpxdivelog) {
+ gpxdivelogs.add(gpxdivelog);
+ super.notifyDataSetChanged();
+    }
+
+ @Override
+ public int getCount() {
+ return gpxdivelogs.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return gpxdivelogs.get(position);
+ }
+
+ public ArrayList<DiveLocationLog> getSelectedDives() {
+ ArrayList<DiveLocationLog> selectedDives = new
ArrayList<DiveLocationLog>();
+ for(int i = 0; i < gpxdivelogs.size(); i++) {
+ if(cbState[i]) {
+ selectedDives.add(gpxdivelogs.get(i));
+ }
+ }
+ return selectedDives;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup
parent) {
+ ViewHolder holder = null;
+ long timestamp;
+         if (convertView == null) {
+             convertView = inflater.inflate(R.layout.gpx_divelistitem,
parent, false);
+             holder = new ViewHolder();
+             holder.tvGpxDiveName = (TextView)
convertView.findViewById(R.id.tvGpxDiveName);
+             holder.tvGpxDiveDate = (TextView)
convertView.findViewById(R.id.tvGpxDiveDate);
+             holder.tvGpxDiveTime = (TextView)
convertView.findViewById(R.id.tvGpxDiveTime);
+             holder.tvGpxDiveLocation = (TextView)
convertView.findViewById(R.id.tvGpxDiveLoc);
+             holder.cbGpxDive = (CheckBox)
convertView.findViewById(R.id.cbGpxDive);
+         } else {
+             holder = (ViewHolder) convertView.getTag();
+             holder.cbGpxDive.setOnCheckedChangeListener(null);
+         }
+         holder.tvGpxDiveName.setText(gpxdivelogs.get(position).getName());
+         timestamp = gpxdivelogs.get(position).getTimestamp();
+         holder.tvGpxDiveDate.setText(sdfdate.format(timestamp));
+         holder.tvGpxDiveTime.setText(sdftime.format(timestamp));
+         holder.cbGpxDive.setChecked(cbState[position]);
+
holder.tvGpxDiveLocation.setText(gpxdivelogs.get(position).getLatitude() +
" , " + gpxdivelogs.get(position).getLongitude());
+         holder.cbGpxDive.setOnCheckedChangeListener(new
OnCheckedChangeListener()
+         {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean
isChecked) {
+ cbState[position] = isChecked;
+ }
+         });
+         convertView.setTag(holder);
+         return convertView;
+ }
+
+ public static class ViewHolder {
+ public TextView tvGpxDiveName;
+ public TextView tvGpxDiveDate;
+ public TextView tvGpxDiveTime;
+ public TextView tvGpxDiveLocation;
+ public CheckBox cbGpxDive;
+ }
+
+}
diff --git a/src/org/subsurface/ui/GpxListAdapter.java
b/src/org/subsurface/ui/GpxListAdapter.java
new file mode 100644
index 0000000..a24b214
--- /dev/null
+++ b/src/org/subsurface/ui/GpxListAdapter.java
@@ -0,0 +1,68 @@
+package org.subsurface.ui;
+
+import java.util.ArrayList;
+
+import org.subsurface.R;
+import org.subsurface.model.GpxFileInfo;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class GpxListAdapter extends BaseAdapter
+{
+ private final ArrayList<GpxFileInfo> gpxfiles;
+ private final LayoutInflater inflater;
+
+ public GpxListAdapter(Context context, ArrayList<GpxFileInfo> gpxfiles) {
+ this.gpxfiles = gpxfiles;
+ inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public void addItem(final GpxFileInfo gpxfileinfo) {
+ gpxfiles.add(gpxfileinfo);
+        super.notifyDataSetChanged();
+    }
+
+ @Override
+ public int getCount() {
+ return gpxfiles.size();
+ }
+ @Override
+ public Object getItem(int position) {
+ return gpxfiles.get(position);
+ }
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder = null;
+         if (convertView == null) {
+             convertView = inflater.inflate(R.layout.gpx_listitem, parent,
false);
+             holder = new ViewHolder();
+             holder.tvName =
(TextView)convertView.findViewById(R.id.tvGpxName);
+             holder.tvDirectory =
(TextView)convertView.findViewById(R.id.tvGpxDirectory);
+             holder.tvDate =
(TextView)convertView.findViewById(R.id.tvGpxDate);
+             convertView.setTag(holder);
+         } else {
+             holder = (ViewHolder)convertView.getTag();
+         }
+         holder.tvName.setText(gpxfiles.get(position).getName());
+         holder.tvDirectory.setText(gpxfiles.get(position).getDirectory());
+         holder.tvDate.setText(gpxfiles.get(position).getDate());
+         return convertView;
+ }
+
+ public static class ViewHolder {
+ public TextView tvName;
+ public TextView tvDirectory;
+ public TextView tvDate;
+ }
+}
+
-- 
1.7.9.5








*Venkatesh Shukla B. Tech  ( Electrical Engineering )III YearIndian
Institute of TechnologyBanaras Hindu UniversityPh No. +91 8960 579 122*
*Email: venkatesh.shukla.eee11 at iitbhu.ac.in
<venkatesh.shukla.eee11 at iitbhu.ac.in>*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.hohndel.org/pipermail/subsurface/attachments/20140309/bfb21186/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Fix-for-bug-423-Import-GPX-file-from-SD-card.patch
Type: text/x-patch
Size: 60436 bytes
Desc: not available
URL: <http://lists.hohndel.org/pipermail/subsurface/attachments/20140309/bfb21186/attachment-0001.bin>


More information about the subsurface mailing list