]> git.gag.com Git - debian/openrocket/commitdiff
Checkpoint commit of android functionality. Application currently consists of simple...
authorkruland2607 <kruland2607@180e2498-e6e9-4542-8430-84ac67f01cd8>
Tue, 3 Jan 2012 02:20:10 +0000 (02:20 +0000)
committerkruland2607 <kruland2607@180e2498-e6e9-4542-8430-84ac67f01cd8>
Tue, 3 Jan 2012 02:20:10 +0000 (02:20 +0000)
Also included is a Motor sqlite database which is populated from thrustcurve downloads.  Includes primitive thrustcurve plotting system.  This will soon be wired into the openrocket application to supply motors for simulation execution.

git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@261 180e2498-e6e9-4542-8430-84ac67f01cd8

64 files changed:
android/.classpath
android/AndroidManifest.xml
android/libs/Androidplot-core-0.4.4-release.jar [new file with mode: 0644]
android/res/drawable-hdpi/arrow_down_float.png [new file with mode: 0644]
android/res/drawable-hdpi/arrow_up_float.png [new file with mode: 0644]
android/res/drawable-ldpi/arrow_down_float.png [new file with mode: 0644]
android/res/drawable-ldpi/arrow_up_float.png [new file with mode: 0644]
android/res/drawable-mdpi/arrow_down_float.png [new file with mode: 0644]
android/res/drawable-mdpi/arrow_up_float.png [new file with mode: 0644]
android/res/layout/main.xml [deleted file]
android/res/layout/motor_burn.xml [new file with mode: 0644]
android/res/layout/motor_detail.xml [new file with mode: 0644]
android/res/layout/motor_detail_form.xml [new file with mode: 0644]
android/res/layout/motor_hierarch_list.xml [new file with mode: 0644]
android/res/layout/motor_list_child.xml [new file with mode: 0644]
android/res/layout/motor_list_group.xml [new file with mode: 0644]
android/res/layout/openrocketviewer.xml [new file with mode: 0644]
android/res/layout/simulation_detail.xml [new file with mode: 0644]
android/res/layout/tcqueryform.xml [new file with mode: 0644]
android/res/menu/motor_browser_option_menu.xml [new file with mode: 0644]
android/res/menu/motor_details_option_menu.xml [new file with mode: 0644]
android/res/menu/rocket_viewer_option_menu.xml [new file with mode: 0644]
android/res/menu/simulation_option_menu.xml [new file with mode: 0644]
android/res/values/strings.xml
android/res/xml/preferences.xml [new file with mode: 0644]
src/net/sf/openrocket/android/Application.java [new file with mode: 0644]
src/net/sf/openrocket/android/LogHelper.java [new file with mode: 0644]
src/net/sf/openrocket/android/Main.java [new file with mode: 0644]
src/net/sf/openrocket/android/PreferencesActivity.java [new file with mode: 0644]
src/net/sf/openrocket/android/PreferencesAdapter.java [new file with mode: 0644]
src/net/sf/openrocket/android/db/DbAdapter.java [new file with mode: 0644]
src/net/sf/openrocket/android/db/MotorDao.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/BurnPlotFragment.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/Motor.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/MotorDetails.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/MotorDetailsFragment.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/MotorHierarchicalBrowser.java [new file with mode: 0644]
src/net/sf/openrocket/android/motor/PersistentExpandableListActivity.java [new file with mode: 0644]
src/net/sf/openrocket/android/rocket/OpenRocketLoaderTask.java [new file with mode: 0644]
src/net/sf/openrocket/android/rocket/OpenRocketViewer.java [new file with mode: 0644]
src/net/sf/openrocket/android/simulation/SimulationPlotFragment.java [new file with mode: 0644]
src/net/sf/openrocket/android/simulation/SimulationViewer.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/Base64Decoder.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/DownloadRequest.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/DownloadResponse.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/DownloadResponseParser.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/MotorBurnFile.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/RSEBurnFile.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/RaspBurnFile.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/SearchRequest.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/SearchResponse.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/SearchResponseParser.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/SupportedFileTypes.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/TCMotor.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/TCQueryActivity.java [new file with mode: 0644]
src/net/sf/openrocket/android/thrustcurve/ThrustCurveAPI.java [new file with mode: 0644]
src/net/sf/openrocket/database/MotorDatabase.java [new file with mode: 0644]
src/net/sf/openrocket/database/ThrustCurveMotorSet.java
src/net/sf/openrocket/database/ThrustCurveMotorSetDatabase.java
src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
src/net/sf/openrocket/motor/Motor.java
src/net/sf/openrocket/motor/MotorDigest.java
src/net/sf/openrocket/motor/ThrustCurveMotor.java
src/net/sf/openrocket/startup/Application.java

index 996577fdf22dc174e3c11c26946446e8b1561fed..1f6c7ff44a25d2597e992dd58b38935d4fc0bba6 100644 (file)
@@ -1,9 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>\r
 <classpath>\r
        <classpathentry kind="src" path="gen"/>\r
-       <classpathentry excluding="net/sf/openrocket/gui/|net/sf/openrocket/utils/|net/sf/openrocket/file/motor/MotorLoaderHelper.java|net/sf/openrocket/material/MaterialStorage.java|net/sf/openrocket/database/Databases.java" kind="src" path="src"/>\r
+       <classpathentry excluding="net/sf/openrocket/file/motor/MotorLoaderHelper.java|net/sf/openrocket/gui/|net/sf/openrocket/startup/Startup.java|net/sf/openrocket/startup/Startup2.java|net/sf/openrocket/startup/VersionHelper.java|net/sf/openrocket/utils/|net/sf/openrocket/file/motor/|net/sf/openrocket/file/CSVExport.java" kind="src" path="src"/>\r
+       <classpathentry kind="src" path="locale"/>\r
        <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>\r
        <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>\r
        <classpathentry kind="lib" path="libs/android-support-v4.jar"/>\r
+       <classpathentry kind="lib" path="libs/Androidplot-core-0.4.4-release.jar"/>\r
        <classpathentry kind="output" path="bin/classes"/>\r
 </classpath>\r
index f1dd120c3e674b32759dff2757048d5a338b599b..1d73587ba49b5e5181f2b34ab5c934e2685585d3 100644 (file)
@@ -1,23 +1,65 @@
 <?xml version="1.0" encoding="utf-8"?>\r
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"\r
-    package="org.openrocket"\r
+    package="net.sf.openrocket"\r
     android:versionCode="1"\r
     android:versionName="1.0" >\r
 \r
-    <uses-sdk android:minSdkVersion="8" />\r
+    <uses-sdk\r
+        android:minSdkVersion="8"\r
+        android:targetSdkVersion="8" />\r
+\r
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />\r
+    <uses-permission android:name="android.permission.INTERNET" />\r
 \r
     <application\r
+        android:debuggable="true"\r
         android:icon="@drawable/ic_launcher"\r
-        android:label="@string/app_name" >\r
-        <activity\r
-            android:label="@string/app_name"\r
-            android:name=".OpenRocketSimulation" >\r
+        android:killAfterRestore="true"\r
+        android:label="@string/app_name"\r
+        android:name=".android.Application" >\r
+        <activity android:name=".android.Main" >\r
             <intent-filter >\r
                 <action android:name="android.intent.action.MAIN" />\r
 \r
                 <category android:name="android.intent.category.LAUNCHER" />\r
             </intent-filter>\r
         </activity>\r
+        <activity\r
+            android:label="@string/app_name"\r
+            android:name=".android.rocket.OpenRocketViewer" >\r
+            <intent-filter >\r
+                <action android:name="android.intent.action.VIEW" />\r
+\r
+                <category android:name="android.intent.category.BROWSABLE" />\r
+                <category android:name="android.intent.category.DEFAULT" />\r
+\r
+                <data\r
+                    android:host="*"\r
+                    android:pathPattern=".*\\.ork"\r
+                    android:scheme="content" />\r
+                <data\r
+                    android:host="*"\r
+                    android:pathPattern=".*\\.ork"\r
+                    android:scheme="file" />\r
+            </intent-filter>\r
+        </activity>\r
+        <activity android:name=".android.PreferencesActivity" >\r
+            <intent-filter >\r
+                <action android:name="net.sf.openrocket.android.PreferencesActivity" />\r
+\r
+                <category android:name="android.intent.category.PREFERENCE" />\r
+            </intent-filter>\r
+        </activity>\r
+        <activity\r
+            android:label="@string/MotorListTitle"\r
+            android:name=".android.motor.MotorHierarchicalBrowser" >\r
+        </activity>\r
+        <activity android:name=".android.motor.MotorDetails" />\r
+        <activity\r
+            android:label="@string/MotorListTitle"\r
+            android:name=".android.thrustcurve.TCQueryActivity" >\r
+        </activity>\r
+        <activity android:name=".android.simulation.SimulationViewer" />\r
     </application>\r
 \r
 </manifest>
\ No newline at end of file
diff --git a/android/libs/Androidplot-core-0.4.4-release.jar b/android/libs/Androidplot-core-0.4.4-release.jar
new file mode 100644 (file)
index 0000000..26d119a
Binary files /dev/null and b/android/libs/Androidplot-core-0.4.4-release.jar differ
diff --git a/android/res/drawable-hdpi/arrow_down_float.png b/android/res/drawable-hdpi/arrow_down_float.png
new file mode 100644 (file)
index 0000000..2466c8f
Binary files /dev/null and b/android/res/drawable-hdpi/arrow_down_float.png differ
diff --git a/android/res/drawable-hdpi/arrow_up_float.png b/android/res/drawable-hdpi/arrow_up_float.png
new file mode 100644 (file)
index 0000000..d1301c3
Binary files /dev/null and b/android/res/drawable-hdpi/arrow_up_float.png differ
diff --git a/android/res/drawable-ldpi/arrow_down_float.png b/android/res/drawable-ldpi/arrow_down_float.png
new file mode 100644 (file)
index 0000000..c41340d
Binary files /dev/null and b/android/res/drawable-ldpi/arrow_down_float.png differ
diff --git a/android/res/drawable-ldpi/arrow_up_float.png b/android/res/drawable-ldpi/arrow_up_float.png
new file mode 100644 (file)
index 0000000..8b60f12
Binary files /dev/null and b/android/res/drawable-ldpi/arrow_up_float.png differ
diff --git a/android/res/drawable-mdpi/arrow_down_float.png b/android/res/drawable-mdpi/arrow_down_float.png
new file mode 100644 (file)
index 0000000..dd82523
Binary files /dev/null and b/android/res/drawable-mdpi/arrow_down_float.png differ
diff --git a/android/res/drawable-mdpi/arrow_up_float.png b/android/res/drawable-mdpi/arrow_up_float.png
new file mode 100644 (file)
index 0000000..9bc3d1c
Binary files /dev/null and b/android/res/drawable-mdpi/arrow_up_float.png differ
diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml
deleted file mode 100644 (file)
index ac9c0ab..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
-    android:layout_width="fill_parent"\r
-    android:layout_height="fill_parent"\r
-    android:orientation="vertical" >\r
-\r
-    <TextView\r
-        android:layout_width="fill_parent"\r
-        android:layout_height="wrap_content"\r
-        android:text="@string/hello" />\r
-\r
-</LinearLayout>
\ No newline at end of file
diff --git a/android/res/layout/motor_burn.xml b/android/res/layout/motor_burn.xml
new file mode 100644 (file)
index 0000000..5b8e3a1
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.androidplot.xy.XYPlot xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/xyplot"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:layout_marginLeft="0px"
+    android:layout_marginRight="0px"
+    android:layout_marginTop="5px"
+    title="plot"
+/>
diff --git a/android/res/layout/motor_detail.xml b/android/res/layout/motor_detail.xml
new file mode 100644 (file)
index 0000000..b36a44a
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <fragment
+        android:id="@+id/burnPlotFragment"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="0px"
+        android:layout_marginRight="0px"
+        android:layout_marginTop="5px"
+        class="net.sf.openrocket.android.motor.BurnPlotFragment"
+        title="Burn Plot" />
+
+    <SlidingDrawer
+        android:id="@+id/drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="0.4"
+        android:content="@+id/motorDetailForm"
+        android:handle="@+id/handle" >
+
+        <ImageView
+            android:id="@+id/handle"
+            android:layout_width="match_parent"
+            android:layout_height="30px"
+            android:src="@drawable/arrow_up_float"
+            android:text="" />
+
+        <fragment
+            android:id="@+id/motorDetailForm"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            class="net.sf.openrocket.android.motor.MotorDetailsFragment" />
+    </SlidingDrawer>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/android/res/layout/motor_detail_form.xml b/android/res/layout/motor_detail_form.xml
new file mode 100644 (file)
index 0000000..45d595d
--- /dev/null
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/motorDetailForm"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black"
+    android:orientation="vertical"
+    android:paddingTop="10px" >
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Manufacturer" />
+
+        <EditText
+            android:id="@+id/motorDetailsManufacturer"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Name" />
+
+        <EditText
+            android:id="@+id/motorDetailsName"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Case Info" />
+
+        <EditText
+            android:id="@+id/motorDetailsCaseInfo"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Impulse Class" />
+
+        <EditText
+            android:id="@+id/motorDetailsImpuseClass"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Diameter" />
+
+        <EditText
+            android:id="@+id/motorDetailsDiameter"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+    <TableRow >
+
+        <TextView
+            android:paddingRight="10px"
+            android:text="Length" />
+
+        <EditText
+            android:id="@+id/motorDetailsLength"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.04"
+            android:editable="false" />
+    </TableRow>
+
+</TableLayout>
\ No newline at end of file
diff --git a/android/res/layout/motor_hierarch_list.xml b/android/res/layout/motor_hierarch_list.xml
new file mode 100644 (file)
index 0000000..9d17a2c
--- /dev/null
@@ -0,0 +1,18 @@
+<?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" >
+
+    <ExpandableListView
+        android:id="@+id/motorListView"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" >
+    </ExpandableListView>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="No motors" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/res/layout/motor_list_child.xml b/android/res/layout/motor_list_child.xml
new file mode 100644 (file)
index 0000000..0b27002
--- /dev/null
@@ -0,0 +1,48 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="45dip"
+    android:gravity="center_vertical"
+    android:orientation="horizontal" >
+
+    <TextView
+        android:id="@+id/motorChildManu"
+        android:layout_width="wrap_content"
+        android:layout_height="45dip"
+        android:layout_alignParentLeft="true"
+        android:gravity="center_vertical"
+        android:paddingLeft="5dip"
+        android:paddingRight="5dip"
+        android:textColor="#ffCCCC22"
+        android:textSize="17dip"
+        android:textStyle="bold" >
+    </TextView>
+
+    <TextView
+        android:id="@+id/motorChildName"
+        android:layout_width="wrap_content"
+        android:layout_height="45dip"
+        android:layout_toRightOf="@id/motorChildManu"
+        android:gravity="center_vertical"
+        android:paddingLeft="5dip"
+        android:paddingRight="5dip"
+        android:text="Motor Name"
+        android:textColor="#ffCCCC22"
+        android:textSize="17dip"
+        android:textStyle="bold" >
+    </TextView>
+
+    <TextView
+        android:id="@+id/motorChildImpulse"
+        android:layout_width="wrap_content"
+        android:layout_height="45dip"
+        android:layout_alignParentRight="true"
+        android:gravity="center_vertical"
+        android:paddingLeft="5dip"
+        android:paddingRight="5dip"
+        android:text="Motor Impulse"
+        android:textColor="#ffCCCC22"
+        android:textSize="17dip"
+        android:textStyle="bold" >
+    </TextView>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/android/res/layout/motor_list_group.xml b/android/res/layout/motor_list_group.xml
new file mode 100644 (file)
index 0000000..a0dbd24
--- /dev/null
@@ -0,0 +1,18 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="45dip" >
+
+    <TextView
+        android:id="@+id/motorGroup"
+        android:layout_width="fill_parent"
+        android:layout_height="45dip"
+        android:gravity="center_vertical|right"
+        android:paddingLeft="5dip"
+        android:paddingRight="5dip"
+        android:text="motor group"
+        android:textColor="#ffffffff"
+        android:textSize="17dip"
+        android:textStyle="bold" >
+    </TextView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/res/layout/openrocketviewer.xml b/android/res/layout/openrocketviewer.xml
new file mode 100644 (file)
index 0000000..1f024e0
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+    android:layout_width="fill_parent"\r
+    android:layout_height="fill_parent"\r
+    android:orientation="vertical" >\r
+\r
+    <TextView\r
+        android:id="@+id/heading"\r
+        android:layout_width="fill_parent"\r
+        android:layout_height="wrap_content"\r
+        android:text="Started" />\r
+\r
+    <ListView\r
+        android:id="@+id/rocketSimulations"\r
+        android:layout_width="match_parent"\r
+        android:layout_height="wrap_content" />\r
+\r
+</LinearLayout>
\ No newline at end of file
diff --git a/android/res/layout/simulation_detail.xml b/android/res/layout/simulation_detail.xml
new file mode 100644 (file)
index 0000000..605f4e7
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <fragment
+        android:id="@+id/simulationPlotFragment"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="0px"
+        android:layout_marginRight="0px"
+        android:layout_marginTop="5px"
+        class="net.sf.openrocket.android.simulation.SimulationPlotFragment"
+        title="Simulation" />
+
+    <SlidingDrawer
+        android:id="@+id/drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="0.4"
+        android:content="@+id/simulationConfigurationForm"
+        android:handle="@+id/handle" >
+
+        <ImageView
+            android:id="@+id/handle"
+            android:layout_width="match_parent"
+            android:layout_height="30px"
+            android:src="@drawable/arrow_up_float"
+            android:text="" />
+
+        <TabHost
+            android:id="@+id/simulationConfigurationForm"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:background="@android:color/black" >
+
+            <LinearLayout
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:orientation="vertical" >
+
+                <TabWidget
+                    android:id="@android:id/tabs"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content" />
+
+                <FrameLayout
+                    android:id="@android:id/tabcontent"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent" >
+
+                    <ListView
+                        android:id="@+id/simulationEventsList"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content" />
+
+                    <ListView
+                        android:id="@+id/simulationSeriesList"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content" />
+                </FrameLayout>
+            </LinearLayout>
+        </TabHost>
+    </SlidingDrawer>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/android/res/layout/tcqueryform.xml b/android/res/layout/tcqueryform.xml
new file mode 100644 (file)
index 0000000..f6fc7c8
--- /dev/null
@@ -0,0 +1,72 @@
+<?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="match_parent"
+    android:orientation="vertical" >
+
+    <TableLayout
+        android:id="@+id/tableLayout1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:stretchColumns="1" >
+
+        <TableRow >
+
+            <TextView
+                android:id="@+id/textView1"
+                android:text="@string/TCMotorSearchFormManufacturer" />
+
+            <Spinner
+                android:id="@+id/TCMotorSearchFormManufacturerField"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:entries="@array/TCMotorSearchManufacturerList" />
+        </TableRow>
+
+        <TableRow >
+
+            <TextView
+                android:id="@+id/textView1"
+                android:text="@string/TCMotorSearchFormImpulse" />
+
+            <Spinner
+                android:id="@+id/TCMotorSearchFormImpulseField"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:entries="@array/TCMotorSearchImpulseList" />
+        </TableRow>
+
+        <TableRow >
+
+            <TextView
+                android:id="@+id/textView1"
+                android:text="@string/TCMotorSearchFormCommonName" />
+
+            <EditText
+                android:id="@+id/TCMotorSearchFormCommonNameField"
+                android:text="" />
+        </TableRow>
+
+        <TableRow >
+
+            <TextView
+                android:id="@+id/textView1"
+                android:text="@string/TCMotorSearchFormDiameter" />
+
+            <Spinner
+                android:id="@+id/TCMotorSearchFormDiameterField"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:entries="@array/TCMotorSearchDiameterList" />
+        </TableRow>
+    </TableLayout>
+
+    <Button
+        android:id="@+id/TCMotorSearchFromSubmitButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerInParent="true"
+        android:text="@string/TCMotorSearchFormSubmit" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/android/res/menu/motor_browser_option_menu.xml b/android/res/menu/motor_browser_option_menu.xml
new file mode 100644 (file)
index 0000000..5c1f1b4
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:title="Download from ThrustCurve" android:id="@+id/download_from_thrustcurve_menu_option"/>
+  <item android:title="Preferences" android:id="@+id/preference_menu_option"/>
+</menu>
diff --git a/android/res/menu/motor_details_option_menu.xml b/android/res/menu/motor_details_option_menu.xml
new file mode 100644 (file)
index 0000000..c2df357
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:enabled="true" android:id="@+id/save" android:visible="true" android:title="@string/save"/>
+    
+</menu>
diff --git a/android/res/menu/rocket_viewer_option_menu.xml b/android/res/menu/rocket_viewer_option_menu.xml
new file mode 100644 (file)
index 0000000..6d4f5c6
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >\r
+
+  <item android:title="Motor List" android:id="@+id/motor_list_menu_option"/>\r
+  <item android:title="Preferences" android:id="@+id/preference_menu_option"/>\r
+    \r
+\r
+</menu>
\ No newline at end of file
diff --git a/android/res/menu/simulation_option_menu.xml b/android/res/menu/simulation_option_menu.xml
new file mode 100644 (file)
index 0000000..f964fb1
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:title="Select Events" android:id="@+id/simulation_select_events_menu_option"/>
+  <item android:title="Select Series" android:id="@+id/simulation_select_series_menu_option"/>
+  <item android:title="Preferences" android:id="@+id/preference_menu_option"/>
+</menu>
index 41bfe9b983702c0c4d1f7dea60a915476a07e918..60e3d8fb2d689d7c26ad1940c042b187c7a3465d 100644 (file)
@@ -1,7 +1,98 @@
 <?xml version="1.0" encoding="utf-8"?>\r
 <resources>\r
 \r
-    <string name="hello">Hello World, OpenRocketSimulation!</string>\r
-    <string name="app_name">OpenRocketSimulation</string>\r
+    <string name="app_name">OpenRocket</string>\r
+    <string name="save">Save</string>\r
+    <string name="MotorListTitle">Motor List</string>\r
+    <string name="PreferenceMotorBrowserGroupingOption">PreferenceMotorBrowserGroupingOption</string>\r
+\r
+    <string-array name="PreferenceMotorBrowserGroupingEntries">\r
+        <item>Case</item>\r
+        <item>Diameter</item>\r
+        <item>Impulse</item>\r
+        <item>Manufacturer</item>\r
+    </string-array>\r
+    <string-array name="PreferenceMotorBrowserGroupingValues">\r
+        <item>0</item>\r
+        <item>1</item>\r
+        <item>2</item>\r
+        <item>3</item>\r
+    </string-array>\r
+\r
+    <string name="TCMotorSearchFormImpulse">Impulse</string>\r
+\r
+    <string-array name="TCMotorSearchImpulseList">\r
+        <item>All</item>\r
+        <item>A</item>\r
+        <item>B</item>\r
+        <item>C</item>\r
+        <item>D</item>\r
+        <item>E</item>\r
+        <item>F</item>\r
+        <item>G</item>\r
+        <item>H</item>\r
+        <item>I</item>\r
+        <item>J</item>\r
+        <item>K</item>\r
+        <item>L</item>\r
+        <item>M</item>\r
+        <item>N</item>\r
+        <item>O</item>\r
+    </string-array>\r
+\r
+    <string name="TCMotorSearchFormManufacturer">Manufacturer</string>\r
+\r
+    <string-array name="TCMotorSearchManufacturerList">\r
+        <item>All</item>\r
+        <item>AeroTech</item>\r
+        <item>Alpha Hybrids</item>\r
+        <item>Animal Motor Works</item>\r
+        <item>Apogee Components</item>\r
+        <item>Cesaroni Technology</item>\r
+        <item>Contrail Rockets</item>\r
+        <item>Ellis Mountain</item>\r
+        <item>Estes Industries</item>\r
+        <item>Gorilla Rocket Motors</item>\r
+        <item>Hypertek</item>\r
+        <item>Kosdon by AeroTech</item>\r
+        <item>Kosdon TRM</item>\r
+        <item>Loki Research</item>\r
+        <item>Propulsion Polymers</item>\r
+        <item>Public Missiles, Ltd.</item>\r
+        <item>Quest Aerospace</item>\r
+        <item>R.A.T.T. Works</item>\r
+        <item>Roadrunner Rocketry</item>\r
+        <item>Rocketvision Flight-Star</item>\r
+        <item>Sky Ripper Systems</item>\r
+        <item>West Coast Hybrids</item>\r
+    </string-array>\r
+\r
+    <string name="TCMotorSearchFormCommonName">Common Name</string>\r
+    <string name="TCMotorSearchFormDiameter">Diameter</string>\r
+\r
+    <string-array name="TCMotorSearchDiameterList">\r
+        <item>All</item>\r
+        <item>6</item>\r
+        <item>10.5</item>\r
+        <item>13</item>\r
+        <item>18</item>\r
+        <item>20</item>\r
+        <item>24</item>\r
+        <item>29</item>\r
+        <item>32</item>\r
+        <item>38</item>\r
+        <item>54</item>\r
+        <item>64</item>\r
+        <item>69</item>\r
+        <item>75</item>\r
+        <item>76</item>\r
+        <item>81</item>\r
+        <item>98</item>\r
+        <item>152</item>\r
+        <item>161</item>\r
+    </string-array>\r
+\r
+    <string name="TCMotorSearchFormSubmit">Submit</string>\r
+    <string name="tcdownload">Download from ThrustCurve</string>\r
 \r
 </resources>
\ No newline at end of file
diff --git a/android/res/xml/preferences.xml b/android/res/xml/preferences.xml
new file mode 100644 (file)
index 0000000..05fc59c
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+  android:key="preferences"
+  android:title="Pref Title"
+  android:summary="pref summary"  
+      
+
+    <ListPreference
+        android:defaultValue="1"
+        android:entries="@array/PreferenceMotorBrowserGroupingEntries"
+        android:entryValues="@array/PreferenceMotorBrowserGroupingValues"
+        android:key="@string/PreferenceMotorBrowserGroupingOption"
+        android:summary="Set the grouping in Motor Browser"
+        android:title="Motor Browser Group" />
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/net/sf/openrocket/android/Application.java b/src/net/sf/openrocket/android/Application.java
new file mode 100644 (file)
index 0000000..61b368c
--- /dev/null
@@ -0,0 +1,73 @@
+package net.sf.openrocket.android;\r
+\r
+import java.util.Locale;\r
+\r
+import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;\r
+import net.sf.openrocket.document.OpenRocketDocument;\r
+import net.sf.openrocket.l10n.DebugTranslator;\r
+import net.sf.openrocket.l10n.ResourceBundleTranslator;\r
+import net.sf.openrocket.l10n.Translator;\r
+\r
+public class Application extends android.app.Application {\r
+\r
+       private OpenRocketDocument rocketDocument;\r
+       \r
+       // Big B boolean so I can synchronize on it.\r
+       private static Boolean initialized = false;\r
+       \r
+       public static void initialize() {\r
+               synchronized (initialized) {\r
+                       if ( initialized == true ) {\r
+                               return;\r
+                       }\r
+\r
+                       // Android does not have a default sax parser set.  This needs to be defined first.\r
+                       System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");\r
+\r
+                       net.sf.openrocket.startup.Application.setLogger( new LogHelper() );\r
+                       \r
+                       net.sf.openrocket.startup.Application.setPreferences( new PreferencesAdapter() );\r
+                       \r
+                       ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(false) {\r
+                               \r
+                               @Override\r
+                               protected void loadMotors() {\r
+                               }\r
+                       };\r
+                       db.startLoading();\r
+\r
+                       net.sf.openrocket.startup.Application.setMotorSetDatabase(db);\r
+                       \r
+                       Translator t;\r
+                       t = new ResourceBundleTranslator("l10n.messages");\r
+                       if (Locale.getDefault().getLanguage().equals("xx")) {\r
+                               t = new DebugTranslator(t);\r
+                       }\r
+                       \r
+                       net.sf.openrocket.startup.Application.setBaseTranslator(t);\r
+\r
+                       initialized = true;\r
+               }\r
+       }\r
+\r
+       public Application() {\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * @return the rocketDocument\r
+        */\r
+       public OpenRocketDocument getRocketDocument() {\r
+               return rocketDocument;\r
+       }\r
+\r
+       /**\r
+        * @param rocketDocument the rocketDocument to set\r
+        */\r
+       public void setRocketDocument(OpenRocketDocument rocketDocument) {\r
+               this.rocketDocument = rocketDocument;\r
+       }\r
+       \r
+       \r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/LogHelper.java b/src/net/sf/openrocket/android/LogHelper.java
new file mode 100644 (file)
index 0000000..ae10d8b
--- /dev/null
@@ -0,0 +1,37 @@
+package net.sf.openrocket.android;\r
+\r
+import android.util.Log;\r
+import net.sf.openrocket.logging.LogLevel;\r
+import net.sf.openrocket.logging.LogLine;\r
+\r
+\r
+public class LogHelper extends net.sf.openrocket.logging.LogHelper {\r
+\r
+       /* (non-Javadoc)\r
+        * @see net.sf.openrocket.logging.LogHelper#log(net.sf.openrocket.logging.LogLine)\r
+        */\r
+       @Override\r
+       public void log(LogLine line) {\r
+               \r
+               LogLevel level = line.getLevel();\r
+               \r
+               switch ( level ) {\r
+               case ERROR:\r
+                       Log.e("OpenRocket", line.toString());\r
+                       break;\r
+               case WARN:\r
+                       Log.w("OpenRocket", line.toString());\r
+                       break;\r
+               case INFO:\r
+                       Log.i("OpenRocket", line.toString());\r
+                       break;\r
+               case DEBUG:\r
+                       Log.d("OpenRocket", line.toString());\r
+                       break;\r
+               default:\r
+                       Log.v("OpenRocket", line.toString());\r
+               }\r
+       }\r
+\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/Main.java b/src/net/sf/openrocket/android/Main.java
new file mode 100644 (file)
index 0000000..07cfe4e
--- /dev/null
@@ -0,0 +1,7 @@
+package net.sf.openrocket.android;\r
+\r
+import android.app.Activity;\r
+\r
+public class Main extends Activity {\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/PreferencesActivity.java b/src/net/sf/openrocket/android/PreferencesActivity.java
new file mode 100644 (file)
index 0000000..4ce0587
--- /dev/null
@@ -0,0 +1,14 @@
+package net.sf.openrocket.android;\r
+\r
+import net.sf.openrocket.R;\r
+import android.os.Bundle;\r
+\r
+public class PreferencesActivity extends android.preference.PreferenceActivity {\r
+\r
+       @Override\r
+       protected void onCreate( Bundle savedInstanceState ) {\r
+               super.onCreate( savedInstanceState );\r
+               addPreferencesFromResource(R.xml.preferences);\r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/PreferencesAdapter.java b/src/net/sf/openrocket/android/PreferencesAdapter.java
new file mode 100644 (file)
index 0000000..086c18c
--- /dev/null
@@ -0,0 +1,96 @@
+package net.sf.openrocket.android;\r
+\r
+import java.util.Collections;\r
+import java.util.Set;\r
+\r
+import net.sf.openrocket.material.Material;\r
+\r
+public class PreferencesAdapter extends net.sf.openrocket.startup.Preferences {\r
+\r
+       @Override\r
+       public boolean getBoolean(String key, boolean defaultValue) {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public void putBoolean(String key, boolean value) {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public int getInt(String key, int defaultValue) {\r
+               // TODO Auto-generated method stub\r
+               return 0;\r
+       }\r
+\r
+       @Override\r
+       public void putInt(String key, int value) {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public double getDouble(String key, double defaultValue) {\r
+               // TODO Auto-generated method stub\r
+               return 0;\r
+       }\r
+\r
+       @Override\r
+       public void putDouble(String key, double value) {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public String getString(String key, String defaultValue) {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void putString(String key, String value) {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public String getString(String directory, String key, String defaultValue) {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void putString(String directory, String key, String value) {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       /* (non-Javadoc)\r
+        * @see net.sf.openrocket.startup.Preferences#addUserMaterial(net.sf.openrocket.material.Material)\r
+        */\r
+       @Override\r
+       public void addUserMaterial(Material m) {\r
+               // TODO Auto-generated method stub\r
+               \r
+       }\r
+\r
+       /* (non-Javadoc)\r
+        * @see net.sf.openrocket.startup.Preferences#getUserMaterials()\r
+        */\r
+       @Override\r
+       public Set<Material> getUserMaterials() {\r
+               return Collections.<Material>emptySet();\r
+       }\r
+\r
+       /* (non-Javadoc)\r
+        * @see net.sf.openrocket.startup.Preferences#removeUserMaterial(net.sf.openrocket.material.Material)\r
+        */\r
+       @Override\r
+       public void removeUserMaterial(Material m) {\r
+               // TODO Auto-generated method stub\r
+               \r
+       }\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/db/DbAdapter.java b/src/net/sf/openrocket/android/db/DbAdapter.java
new file mode 100644 (file)
index 0000000..4de12fa
--- /dev/null
@@ -0,0 +1,81 @@
+package net.sf.openrocket.android.db;\r
+\r
+import android.content.Context;\r
+import android.database.SQLException;\r
+import android.database.sqlite.SQLiteDatabase;\r
+import android.database.sqlite.SQLiteOpenHelper;\r
+import android.util.Log;\r
+\r
+public class DbAdapter {\r
+\r
+    private static final String TAG = "DbAdapter";\r
+    private DatabaseHelper mDbHelper;\r
+    private SQLiteDatabase mDb;\r
+\r
+    private static final String DATABASE_NAME = "rocketflightnotebook";\r
+    private static final int DATABASE_VERSION = 5;\r
+\r
+    private final Context mCtx;\r
+\r
+    private MotorDao motorDao;\r
+    \r
+    public MotorDao getMotorDao() {\r
+               return motorDao;\r
+       }\r
+    \r
+       private class DatabaseHelper extends SQLiteOpenHelper {\r
+        DatabaseHelper(Context context) {\r
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);\r
+        }\r
+\r
+        @Override\r
+        public void onCreate(SQLiteDatabase db) {\r
+               executeSQL( db, MotorDao.create());\r
+        }\r
+        \r
+        @Override\r
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\r
+            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "\r
+                    + newVersion + ", which will destroy all old data");\r
+            executeSQL(db, MotorDao.update(oldVersion, newVersion));\r
+        }\r
+\r
+        private void executeSQL( SQLiteDatabase db, String[] sqls ) {\r
+               for(String s: sqls ) {\r
+                       db.execSQL(s);\r
+               }\r
+        }\r
+\r
+    }\r
\r
+    /**\r
+     * Constructor - takes the context to allow the database to be\r
+     * opened/created\r
+     * \r
+     * @param ctx the Context within which to work\r
+     */\r
+    public DbAdapter(Context ctx) {\r
+        this.mCtx = ctx;\r
+    }\r
+\r
+    /**\r
+     * Open the database. If it cannot be opened, try to create a new\r
+     * instance of the database. If it cannot be created, throw an exception to\r
+     * signal the failure\r
+     * \r
+     * @return this (self reference, allowing this to be chained in an\r
+     *         initialization call)\r
+     * @throws SQLException if the database could be neither opened or created\r
+     */\r
+    public DbAdapter open() throws SQLException {\r
+        mDbHelper = new DatabaseHelper(mCtx);\r
+        mDb = mDbHelper.getWritableDatabase();\r
+        motorDao = new MotorDao(mDb);\r
+        return this;\r
+    }\r
+\r
+    public void close() {\r
+        mDbHelper.close();\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/net/sf/openrocket/android/db/MotorDao.java b/src/net/sf/openrocket/android/db/MotorDao.java
new file mode 100644 (file)
index 0000000..7da2598
--- /dev/null
@@ -0,0 +1,270 @@
+package net.sf.openrocket.android.db;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.ObjectInputStream;\r
+import java.io.ObjectOutputStream;\r
+import java.util.Vector;\r
+\r
+import net.sf.openrocket.android.motor.Motor;\r
+import android.content.ContentValues;\r
+import android.database.Cursor;\r
+import android.database.SQLException;\r
+import android.database.sqlite.SQLiteDatabase;\r
+import android.util.Log;\r
+\r
+public class MotorDao {\r
+\r
+       private static final String TAG = "MotorDao";\r
+\r
+       private SQLiteDatabase mDb;\r
+\r
+       private final static String DATABASE_TABLE = "motor";\r
+       private final static String DROP_TABLE = "DROP TABLE IF EXISTS " + DATABASE_TABLE;\r
+       private final static String CREATE_TABLE =\r
+                       "create table "+ DATABASE_TABLE + " ( " +\r
+                                       "_id integer primary key, "+\r
+                                       "unique_name text unique, "+\r
+                                       "name text, "+\r
+                                       "diameter number, "+\r
+                                       "tot_impulse_ns number, "+\r
+                                       "avg_thrust_n number, "+\r
+                                       "max_thrust_n number, "+\r
+                                       "burn_time_s number, "+\r
+                                       "length number," +\r
+                                       "prop_mass_g number,"+\r
+                                       "tot_mass_g number,"+\r
+                                       "case_info text,"+\r
+                                       "manufacturer text," +\r
+                                       "impulse_class text," +\r
+                                       "burndata blob"+\r
+                                       ");";\r
+\r
+       MotorDao( SQLiteDatabase mDb ) {\r
+               this.mDb = mDb;\r
+       }\r
+\r
+       static String[] create() { return new String[] {CREATE_TABLE}; }\r
+\r
+       static String[] update( int oldVer, int newVer ) {\r
+               return new String[] { DROP_TABLE, CREATE_TABLE };\r
+       }\r
+\r
+       public final static String ID = "_id";\r
+       public final static String UNIQUE_NAME = "unique_name";\r
+       public final static String NAME = "name";\r
+       public final static String DIAMETER = "diameter";\r
+       public final static String TOTAL_IMPULSE = "tot_impulse_ns"; \r
+       public final static String AVG_THRUST = "avg_thrust_n";\r
+       public final static String MAX_THRUST = "max_thrust_n";\r
+       public final static String BURN_TIME = "burn_time_s";\r
+       public final static String LENGTH = "length";\r
+       public final static String PROP_MASS = "prop_mass_g";\r
+       public final static String TOT_MASS = "tot_mass_g";\r
+       public final static String BURNDATA = "burndata";\r
+       public final static String CASE_INFO = "case_info";\r
+       public final static String MANUFACTURER = "manufacturer";\r
+       public final static String IMPULSE_CLASS = "impulse_class";\r
+\r
+       public long insertOrUpdateMotor(Motor mi) {\r
+               ContentValues initialValues = new ContentValues();\r
+               initialValues.put(ID, mi.getMotor_id());\r
+               initialValues.put(NAME, mi.getName());\r
+               initialValues.put(DIAMETER,mi.getDiameter());\r
+               initialValues.put(TOTAL_IMPULSE,mi.getTotalImpulse());\r
+               initialValues.put(AVG_THRUST,mi.getAvgThrust());\r
+               initialValues.put(MAX_THRUST,mi.getMaxThrust());\r
+               initialValues.put(BURN_TIME,mi.getBurnTime());\r
+               initialValues.put(LENGTH, mi.getLength());\r
+               initialValues.put(PROP_MASS, mi.getPropMass());\r
+               initialValues.put(TOT_MASS,mi.getTotMass());\r
+               initialValues.put(CASE_INFO, mi.getCaseInfo());\r
+               initialValues.put(MANUFACTURER,mi.getManufacturer());\r
+               initialValues.put(IMPULSE_CLASS,mi.getImpulseClass());\r
+               initialValues.put(UNIQUE_NAME, mi.getManufacturer()+mi.getName());\r
+               {\r
+                       // Serialize the Vector of burn data\r
+                       Vector<Double> burndata = mi.getBurndata();\r
+                       byte[] serObj = null;\r
+                       if ( burndata != null ) {\r
+                               try {\r
+                                       ByteArrayOutputStream b = new ByteArrayOutputStream();\r
+                                       ObjectOutputStream os = new ObjectOutputStream(b);\r
+                                       os.writeObject(burndata);\r
+                                       os.close();\r
+                                       serObj = b.toByteArray();\r
+                               } catch (Exception ex) {\r
+                                       Log.d(TAG,"unable to serialze burndata");\r
+                               }\r
+                       }\r
+                       initialValues.put(BURNDATA, serObj);\r
+               }\r
+\r
+\r
+               Log.d(TAG,"insertOrUpdate Motor");\r
+               long rv = mDb.insertWithOnConflict(DATABASE_TABLE, null, initialValues,SQLiteDatabase.CONFLICT_REPLACE);\r
+               return rv;\r
+       }\r
+\r
+       /**\r
+        * Delete the motor and burn data with the given rowId\r
+        * \r
+        * @param name name of motor to delete\r
+        * @return true if deleted, false otherwise\r
+        */\r
+       public boolean deleteMotor(Long id) {\r
+\r
+               boolean rv =  mDb.delete(DATABASE_TABLE, ID + "=" + id, null) > 0;\r
+               return rv;\r
+       }\r
+\r
+       /**\r
+        * \r
+        * @param groupCol\r
+        * @param groupVal\r
+        * @return\r
+        */\r
+       public Cursor fetchAllInGroups( String groupCol, String groupVal ) {\r
+               return mDb.query(DATABASE_TABLE, \r
+                               /* columns */new String[] {\r
+                               ID,\r
+                               NAME,\r
+                               DIAMETER ,\r
+                               TOTAL_IMPULSE,\r
+                               AVG_THRUST ,\r
+                               MAX_THRUST ,\r
+                               BURN_TIME ,\r
+                               LENGTH,\r
+                               PROP_MASS,\r
+                               TOT_MASS,\r
+                               CASE_INFO,\r
+                               IMPULSE_CLASS,\r
+                               MANUFACTURER\r
+               },\r
+               /* selection */groupCol + "=?",\r
+               /* selection args*/new String[] {groupVal},\r
+               /* groupby */null,\r
+               /* having*/null,\r
+               /* orderby*/ NAME );\r
+\r
+       }\r
+\r
+       /**\r
+        * Fetch the groups based on groupCol\r
+        * @param groupCol\r
+        * @return\r
+        */\r
+       public Cursor fetchGroups( String groupCol ) {\r
+               return mDb.query(true, DATABASE_TABLE, \r
+                               /* columns */new String[] {\r
+                               groupCol\r
+               },\r
+               /* selection */null,\r
+               /* selection args*/null,\r
+               /* groupby */null,\r
+               /* having*/null,\r
+               /* orderby*/null,\r
+               /* limit*/ null);\r
+\r
+       }\r
+\r
+       /**\r
+        * Return a Cursor over the list of all motors\r
+        * \r
+        * @return Cursor over all notes\r
+        */\r
+       public Cursor fetchAllMotors() {\r
+\r
+               return mDb.query(DATABASE_TABLE, \r
+                               /* columns */new String[] {\r
+                               ID,\r
+                               NAME,\r
+                               DIAMETER ,\r
+                               TOTAL_IMPULSE,\r
+                               AVG_THRUST ,\r
+                               MAX_THRUST ,\r
+                               BURN_TIME ,\r
+                               LENGTH,\r
+                               PROP_MASS,\r
+                               TOT_MASS,\r
+                               CASE_INFO,\r
+                               IMPULSE_CLASS,\r
+                               MANUFACTURER\r
+               },\r
+               /* selection */null,\r
+               /* selection args*/null,\r
+               /* groupby */null,\r
+               /* having*/null,\r
+               /* orderby*/null);\r
+       }\r
+\r
+       public Motor fetchMotor(Long id ) throws SQLException {\r
+               Cursor mCursor = mDb.query(DATABASE_TABLE, \r
+                               /* columns */new String[] {\r
+                               ID,\r
+                               NAME ,\r
+                               DIAMETER ,\r
+                               TOTAL_IMPULSE ,\r
+                               AVG_THRUST ,\r
+                               MAX_THRUST ,\r
+                               BURN_TIME ,\r
+                               LENGTH,\r
+                               PROP_MASS,\r
+                               TOT_MASS,\r
+                               CASE_INFO,\r
+                               IMPULSE_CLASS,\r
+                               MANUFACTURER,\r
+                               BURNDATA\r
+               },\r
+               /* selection */ID + "="+id,\r
+               /* selection args*/null,\r
+               /* groupby */null,\r
+               /* having*/null,\r
+               /* orderby*/null);\r
+               if ( mCursor == null ) {\r
+                       return null;\r
+               }\r
+               try {\r
+                       if (mCursor.getCount() == 0) {\r
+                               return null;\r
+                       }\r
+                       mCursor.moveToFirst();\r
+                       Motor mi = new Motor();\r
+                       mi.setMotor_id(mCursor.getLong(mCursor.getColumnIndex(ID)));\r
+                       mi.setName(mCursor.getString(mCursor.getColumnIndex(NAME)));\r
+                       mi.setDiameter(mCursor.getLong(mCursor.getColumnIndex(DIAMETER)));\r
+                       mi.setTotalImpulse(mCursor.getFloat(mCursor.getColumnIndex(TOTAL_IMPULSE)));\r
+                       mi.setAvgThrust(mCursor.getFloat(mCursor.getColumnIndex(AVG_THRUST)));\r
+                       mi.setMaxThrust(mCursor.getFloat(mCursor.getColumnIndex(MAX_THRUST)));\r
+                       mi.setBurnTime(mCursor.getFloat(mCursor.getColumnIndex(BURN_TIME)));\r
+                       mi.setLength(mCursor.getFloat(mCursor.getColumnIndex(LENGTH)));\r
+                       mi.setPropMass(mCursor.getDouble(mCursor.getColumnIndex(PROP_MASS)));\r
+                       mi.setCaseInfo(mCursor.getString(mCursor.getColumnIndex(CASE_INFO)));\r
+                       mi.setTotMass(mCursor.getDouble(mCursor.getColumnIndex(TOT_MASS)));\r
+                       mi.setManufacturer(mCursor.getString(mCursor.getColumnIndex(MANUFACTURER)));\r
+                       mi.setImpulseClass(mCursor.getString(mCursor.getColumnIndex(IMPULSE_CLASS)));\r
+\r
+                       {\r
+                               // Deserialize burndata column\r
+                               byte[] serObj = mCursor.getBlob(mCursor.getColumnIndex(BURNDATA));\r
+                               Vector<Double> burndata = null;\r
+                               if (serObj != null ) {\r
+                                       try {\r
+                                               ObjectInputStream is = new ObjectInputStream( new ByteArrayInputStream(serObj));\r
+                                               burndata = (Vector<Double>) is.readObject();\r
+                                       }\r
+                                       catch (Exception ex) {\r
+                                               Log.d(TAG,"cannot deserialize burndata");\r
+                                       }\r
+                               }\r
+                               mi.setBurndata(burndata);\r
+                       }\r
+                       return mi;\r
+               }\r
+               finally {\r
+                       mCursor.close();\r
+               }\r
+\r
+       }\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/motor/BurnPlotFragment.java b/src/net/sf/openrocket/android/motor/BurnPlotFragment.java
new file mode 100644 (file)
index 0000000..1a8df46
--- /dev/null
@@ -0,0 +1,243 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import java.util.Vector;\r
+\r
+import net.sf.openrocket.R;\r
+import android.app.Activity;\r
+import android.graphics.Color;\r
+import android.graphics.PointF;\r
+import android.os.Bundle;\r
+import android.support.v4.app.Fragment;\r
+import android.util.Log;\r
+import android.view.LayoutInflater;\r
+import android.view.MotionEvent;\r
+import android.view.ScaleGestureDetector;\r
+import android.view.View;\r
+import android.view.View.OnTouchListener;\r
+import android.view.ViewGroup;\r
+\r
+import com.androidplot.xy.BoundaryMode;\r
+import com.androidplot.xy.LineAndPointFormatter;\r
+import com.androidplot.xy.LineAndPointRenderer;\r
+import com.androidplot.xy.SimpleXYSeries;\r
+import com.androidplot.xy.XYPlot;\r
+import com.androidplot.xy.YValueMarker;\r
+\r
+public class BurnPlotFragment extends Fragment implements OnTouchListener {\r
+\r
+       private final static String TAG = "BurnPlotFragment";\r
+\r
+       private XYPlot mySimpleXYPlot;\r
+       private SimpleXYSeries mySeries;\r
+       private PointF minXY;\r
+       private PointF maxXY;\r
+       \r
+       private float absMinX;\r
+       private float absMaxX;\r
+       private float minNoError;\r
+       private float maxNoError;\r
+\r
+       private ScaleGestureDetector mScaleDetector;\r
+       private float mScaleFactor = 1.f;\r
+\r
+       public static BurnPlotFragment initializeBurnPlotHelper( Motor motor ) {\r
+               BurnPlotFragment h = new BurnPlotFragment();\r
+\r
+               Bundle args = new Bundle();\r
+               args.putSerializable("Motor", motor);\r
+               h.setArguments(args);\r
+               return h;\r
+       }\r
+\r
+       @Override\r
+       public void onAttach(Activity activity) {\r
+               super.onAttach(activity);\r
+               Log.d(TAG,"onAttach");\r
+       }\r
+\r
+       @Override\r
+       public void onCreate(Bundle savedInstanceState) {\r
+               Log.d(TAG,"onCreate");\r
+               super.onCreate(savedInstanceState);\r
+       }\r
+\r
+       @Override\r
+       public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
+                       Bundle savedInstanceState) {\r
+               Log.d(TAG,"onCreateView");\r
+               View v = inflater.inflate(R.layout.motor_burn, container, false);\r
+               mySimpleXYPlot = (XYPlot) v.findViewById(R.id.xyplot);\r
+               mySimpleXYPlot.setOnTouchListener(this);\r
+               mScaleDetector = new ScaleGestureDetector(v.getContext(), new ScaleListener());\r
+               //              Motor motor = getMotor();\r
+               //              init(motor);\r
+               return v;\r
+       }\r
+\r
+       void init( Motor motor ) {\r
+\r
+               mySimpleXYPlot.setUserDomainOrigin(0);\r
+               mySimpleXYPlot.setUserRangeOrigin(0);\r
+               mySimpleXYPlot.setRangeLabel("impuse (n)");\r
+               mySimpleXYPlot.setDomainLabel("time (s)");\r
+               mySimpleXYPlot.addMarker(new YValueMarker(motor.getAvgThrust(),"average" ));\r
+               mySimpleXYPlot.disableAllMarkup();\r
+\r
+               Vector<Double> data = null;\r
+               try {\r
+                       data = motor.getBurndata();\r
+               } catch ( Exception ex ) {\r
+               }\r
+               if ( data == null || data.size() == 0 ) {\r
+                       data = new Vector<Double>();\r
+                       data.add(0.0);\r
+                       data.add(0.0);\r
+                       data.add(1.0);\r
+                       data.add(1.0);\r
+               }\r
+               Log.d("plot","data = " + data.toString());\r
+\r
+               mySeries = new SimpleXYSeries(data, SimpleXYSeries.ArrayFormat.XY_VALS_INTERLEAVED,motor.getName());\r
+\r
+               mySimpleXYPlot.addSeries(mySeries, LineAndPointRenderer.class,\r
+                               new LineAndPointFormatter(Color.rgb(0, 255, 0), Color.rgb(200, 0, 0), null));\r
+\r
+               //Set of internal variables for keeping track of the boundaries\r
+               mySimpleXYPlot.calculateMinMaxVals();\r
+               \r
+               mySimpleXYPlot.redraw();\r
+\r
+               minXY=new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),mySimpleXYPlot.getCalculatedMinY().floatValue());\r
+               maxXY=new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),mySimpleXYPlot.getCalculatedMaxY().floatValue());\r
+\r
+               absMinX = minXY.x;\r
+               absMaxX = maxXY.x;\r
+               \r
+               minNoError = Math.round(mySeries.getX(1).floatValue() +2);\r
+               maxNoError = Math.round(mySeries.getX(mySeries.size() -1).floatValue()) - 2.0f;\r
+       }\r
+\r
+       private float mPosX;\r
+       private float mPosY;\r
+\r
+       private float mLastTouchX;\r
+       private float mLastTouchY;\r
+\r
+       private int mActivePointerId = -1;\r
+\r
+       @Override\r
+       public boolean onTouch(View arg0, MotionEvent event) {\r
+               mScaleDetector.onTouchEvent(event);\r
+\r
+               final int action = event.getAction();\r
+               switch ( action & MotionEvent.ACTION_MASK ) {\r
+               case MotionEvent.ACTION_DOWN: {\r
+                       final float x = event.getX();\r
+                       final float y = event.getY();\r
+\r
+                       mLastTouchX = x;\r
+                       mLastTouchY = y;\r
+\r
+                       mActivePointerId = event.getPointerId(0);\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_MOVE: {\r
+                       final int pointerIndex = event.findPointerIndex(mActivePointerId);\r
+                       final float x = event.getX(pointerIndex);\r
+                       final float y = event.getY(pointerIndex);\r
+\r
+                       if (!mScaleDetector.isInProgress()) {\r
+                               final float dx = x - mLastTouchX;\r
+                               final float dy = y - mLastTouchY;\r
+                       \r
+                               mPosX += dx;\r
+                               mPosY += dy;\r
+                               scroll(dx);\r
+                               // do scroll.\r
+                       \r
+                       }\r
+                       mLastTouchX = x;\r
+                       mLastTouchY = y;\r
+                       \r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_UP: {\r
+                       mActivePointerId = -1;\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_CANCEL: {\r
+                       mActivePointerId = -1;\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_POINTER_UP: {\r
+                       final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;\r
+                       final int pointerId = event.getPointerId(pointerIndex);\r
+                       if (pointerId == mActivePointerId) {\r
+                               // This was our active pointer going up.  choose a new active pointer and adjust accordingly.\r
+                               final int newPointerIndex = pointerIndex ==0 ? 1:0;\r
+                               mLastTouchX = event.getX(newPointerIndex);\r
+                               mLastTouchY = event.getY(newPointerIndex);\r
+                               mActivePointerId = event.getPointerId(newPointerIndex);\r
+                       }\r
+                       break;\r
+               }\r
+               }       \r
+               return true;\r
+       }\r
+\r
+       private void zoom(float scale) {\r
+               Log.d(TAG,"zoom by " + scale);\r
+               float domainSpan = absMaxX      - absMinX;\r
+               Log.d(TAG,"domainSpan = " + domainSpan);\r
+               float domainMidPoint = absMaxX          - domainSpan / 2.0f;\r
+               Log.d(TAG,"domainMidPoint = " + domainMidPoint);\r
+               float offset = domainSpan / scale;\r
+               Log.d(TAG,"offset " + offset);\r
+               minXY.x=domainMidPoint- offset;\r
+               Log.d(TAG,"min X " + minXY.x);\r
+               maxXY.x=domainMidPoint+offset;\r
+               Log.d(TAG,"max X " + maxXY.x);\r
+               checkBoundaries();\r
+               mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);\r
+               mySimpleXYPlot.redraw();\r
+       }\r
+\r
+       private void scroll(float pan) {\r
+               float domainSpan = maxXY.x      - minXY.x;\r
+               float step = domainSpan / mySimpleXYPlot.getWidth();\r
+               float offset = pan * step;\r
+               minXY.x+= offset;\r
+               maxXY.x+= offset;\r
+               checkBoundaries();\r
+               mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);\r
+               mySimpleXYPlot.redraw();\r
+       }\r
+\r
+       private void checkBoundaries() {\r
+               \r
+               if ( minXY.x < absMinX) \r
+                       minXY.x = absMinX;\r
+//             else if ( minXY.x > maxNoError )\r
+//                     minXY.x = maxNoError;\r
+               \r
+               if ( maxXY.x > absMaxX)\r
+                       maxXY.x = absMaxX;\r
+//             else if ( maxXY.x < minNoError)\r
+//                     maxXY.x = minNoError;\r
+       }\r
+       private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {\r
+               @Override\r
+               public boolean onScale( ScaleGestureDetector detector ) {\r
+                       mScaleFactor *= detector.getScaleFactor();\r
+\r
+                       mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));\r
+                       zoom(mScaleFactor);\r
+                       return true;\r
+               }\r
+       }\r
+}\r
+\r
diff --git a/src/net/sf/openrocket/android/motor/Motor.java b/src/net/sf/openrocket/android/motor/Motor.java
new file mode 100644 (file)
index 0000000..68bd113
--- /dev/null
@@ -0,0 +1,107 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import java.io.Serializable;\r
+import java.util.Vector;\r
+\r
+public class Motor implements Serializable {\r
+       \r
+       private Long motor_id;\r
+       private String name;\r
+       private String impulseClass;\r
+       private String manufacturer;\r
+       private Long diameter;\r
+       private String caseInfo;\r
+       private Float avgThrust;\r
+       private Float maxThrust;\r
+       private Float totalImpulse;\r
+       private Float burnTime;\r
+       private Float length;\r
+       private Double propMass;\r
+       private Double totMass;\r
+       private Vector<Double> burndata;\r
+       public Long getMotor_id() {\r
+               return motor_id;\r
+       }\r
+       public void setMotor_id(Long motor_id) {\r
+               this.motor_id = motor_id;\r
+       }\r
+       public String getName() {\r
+               return name;\r
+       }\r
+       public void setName(String name) {\r
+               this.name = name;\r
+       }\r
+       public String getImpulseClass() {\r
+               return impulseClass;\r
+       }\r
+       public void setImpulseClass(String impulseClass) {\r
+               this.impulseClass = impulseClass;\r
+       }\r
+       public String getManufacturer() {\r
+               return manufacturer;\r
+       }\r
+       public void setManufacturer(String manufacturer) {\r
+               this.manufacturer = manufacturer;\r
+       }\r
+       public Long getDiameter() {\r
+               return diameter;\r
+       }\r
+       public void setDiameter(Long diameter) {\r
+               this.diameter = diameter;\r
+       }\r
+       public String getCaseInfo() {\r
+               return caseInfo;\r
+       }\r
+       public void setCaseInfo(String caseInfo) {\r
+               this.caseInfo = caseInfo;\r
+       }\r
+       public Float getAvgThrust() {\r
+               return avgThrust;\r
+       }\r
+       public void setAvgThrust(Float avgThrust) {\r
+               this.avgThrust = avgThrust;\r
+       }\r
+       public Float getMaxThrust() {\r
+               return maxThrust;\r
+       }\r
+       public void setMaxThrust(Float maxThrust) {\r
+               this.maxThrust = maxThrust;\r
+       }\r
+       public Float getTotalImpulse() {\r
+               return totalImpulse;\r
+       }\r
+       public void setTotalImpulse(Float totalImpulse) {\r
+               this.totalImpulse = totalImpulse;\r
+       }\r
+       public Float getBurnTime() {\r
+               return burnTime;\r
+       }\r
+       public void setBurnTime(Float burnTime) {\r
+               this.burnTime = burnTime;\r
+       }\r
+       public Float getLength() {\r
+               return length;\r
+       }\r
+       public void setLength(Float length) {\r
+               this.length = length;\r
+       }\r
+       public Double getPropMass() {\r
+               return propMass;\r
+       }\r
+       public void setPropMass(Double propMass) {\r
+               this.propMass = propMass;\r
+       }\r
+       public Double getTotMass() {\r
+               return totMass;\r
+       }\r
+       public void setTotMass(Double totMass) {\r
+               this.totMass = totMass;\r
+       }\r
+       public Vector<Double> getBurndata() {\r
+               return burndata;\r
+       }\r
+       public void setBurndata(Vector<Double> burndata) {\r
+               this.burndata = burndata;\r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/motor/MotorDetails.java b/src/net/sf/openrocket/android/motor/MotorDetails.java
new file mode 100644 (file)
index 0000000..19f7548
--- /dev/null
@@ -0,0 +1,77 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import net.sf.openrocket.R;\r
+import android.content.Intent;\r
+import android.os.Bundle;\r
+import android.support.v4.app.FragmentActivity;\r
+import android.util.Log;\r
+import android.view.Menu;\r
+import android.view.MenuInflater;\r
+import android.view.MenuItem;\r
+import android.widget.ImageView;\r
+import android.widget.SlidingDrawer;\r
+\r
+public class MotorDetails extends FragmentActivity\r
+implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListener {\r
+\r
+       private final static String TAG = "MotorDetails";\r
+       \r
+       private SlidingDrawer slidingDrawer;\r
+       private ImageView handle;\r
+       \r
+       private Motor motor;\r
+       \r
+       @Override\r
+       public void onCreate(Bundle savedInstanceState) {\r
+               super.onCreate(savedInstanceState);\r
+               Log.d(TAG,"onCreate Bundle = "+ String.valueOf(savedInstanceState));\r
+               setContentView(R.layout.motor_detail);\r
+\r
+               Intent i = getIntent();\r
+               motor = (Motor) i.getSerializableExtra("Motor");\r
+               \r
+               BurnPlotFragment burnPlot = (BurnPlotFragment) getSupportFragmentManager().findFragmentById(R.id.burnPlotFragment);\r
+               burnPlot.init(motor);\r
+               \r
+               MotorDetailsFragment motorDetails = (MotorDetailsFragment) getSupportFragmentManager().findFragmentById(R.id.motorDetailForm);\r
+               motorDetails.init(motor);\r
+               \r
+               slidingDrawer = (SlidingDrawer) findViewById(R.id.drawer);\r
+               \r
+               slidingDrawer.setOnDrawerOpenListener(this);\r
+               slidingDrawer.setOnDrawerCloseListener(this);\r
+               \r
+               handle = (ImageView) findViewById(R.id.handle);\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void onDrawerOpened() {\r
+               handle.setImageResource(R.drawable.arrow_down_float);\r
+       }\r
+       \r
+       @Override\r
+       public void onDrawerClosed() {\r
+               handle.setImageResource(R.drawable.arrow_up_float);\r
+       }\r
+\r
+       @Override\r
+       public boolean onCreateOptionsMenu(Menu menu) {\r
+       MenuInflater inflater = getMenuInflater();\r
+       inflater.inflate(R.menu.motor_details_option_menu, menu);\r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       public boolean onMenuItemSelected(int featureId, MenuItem item) {\r
+               switch(item.getItemId()) {\r
+               case R.id.save:\r
+                       // Extract form data to Motor.\r
+                       // Save motor.\r
+                       return true;\r
+               }\r
+               return super.onMenuItemSelected(featureId, item);\r
+       }\r
+\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/motor/MotorDetailsFragment.java b/src/net/sf/openrocket/android/motor/MotorDetailsFragment.java
new file mode 100644 (file)
index 0000000..eca5d9c
--- /dev/null
@@ -0,0 +1,43 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import net.sf.openrocket.R;\r
+import android.os.Bundle;\r
+import android.support.v4.app.Fragment;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.EditText;\r
+\r
+public class MotorDetailsFragment extends Fragment {\r
+\r
+       EditText manuField;\r
+       EditText nameField;\r
+       EditText caseField;\r
+       EditText impulseClassField;\r
+       EditText diameterField;\r
+       EditText lengthField;\r
+       \r
+       @Override\r
+       public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
+                       Bundle savedInstanceState) {\r
+               View v = inflater.inflate(R.layout.motor_detail_form, container, false);\r
+               manuField = (EditText) v.findViewById(R.id.motorDetailsManufacturer);\r
+               nameField = (EditText) v.findViewById(R.id.motorDetailsName);\r
+               caseField = (EditText) v.findViewById(R.id.motorDetailsCaseInfo);\r
+               impulseClassField = (EditText) v.findViewById(R.id.motorDetailsImpuseClass);\r
+               diameterField = (EditText) v.findViewById(R.id.motorDetailsDiameter);\r
+               lengthField = (EditText) v.findViewById(R.id.motorDetailsLength);\r
+               return v;\r
+       }\r
+\r
+       public void init( Motor m ) {\r
+               manuField.setText( m.getManufacturer());\r
+               nameField.setText( m.getName() );\r
+               caseField.setText( m.getCaseInfo());\r
+               impulseClassField.setText( m.getImpulseClass());\r
+               diameterField.setText( m.getDiameter().toString() );\r
+               lengthField.setText( m.getLength().toString() );\r
+               \r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/motor/MotorHierarchicalBrowser.java b/src/net/sf/openrocket/android/motor/MotorHierarchicalBrowser.java
new file mode 100644 (file)
index 0000000..8344083
--- /dev/null
@@ -0,0 +1,231 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import net.sf.openrocket.R;\r
+import net.sf.openrocket.android.PreferencesActivity;\r
+import net.sf.openrocket.android.db.DbAdapter;\r
+import net.sf.openrocket.android.db.MotorDao;\r
+import net.sf.openrocket.android.thrustcurve.TCQueryActivity;\r
+import android.content.Context;\r
+import android.content.Intent;\r
+import android.content.SharedPreferences;\r
+import android.content.res.Resources;\r
+import android.database.Cursor;\r
+import android.os.Bundle;\r
+import android.preference.PreferenceManager;\r
+import android.util.Log;\r
+import android.view.ContextMenu;\r
+import android.view.ContextMenu.ContextMenuInfo;\r
+import android.view.Menu;\r
+import android.view.MenuInflater;\r
+import android.view.MenuItem;\r
+import android.view.View;\r
+import android.widget.CursorTreeAdapter;\r
+import android.widget.ExpandableListView;\r
+import android.widget.SimpleCursorTreeAdapter;\r
+\r
+\r
+public class MotorHierarchicalBrowser\r
+extends PersistentExpandableListActivity\r
+implements SharedPreferences.OnSharedPreferenceChangeListener\r
+{\r
+       private static final String TAG = "MotorHierarchicalBrowser";\r
+\r
+       private static final int ACTIVITY_DOWNLOAD=0;\r
+\r
+       private static final int CONTEXTMENU_DELETE = Menu.FIRST+1;\r
+\r
+       private String groupColumnPreferenceKey;\r
+       private String groupColumn = MotorDao.CASE_INFO;\r
+\r
+       private static final String[] groupColumns = new String[] {\r
+               MotorDao.CASE_INFO,\r
+               MotorDao.DIAMETER,\r
+               MotorDao.IMPULSE_CLASS,\r
+               MotorDao.MANUFACTURER\r
+       };\r
+\r
+       private CursorTreeAdapter mAdapter;\r
+\r
+       private DbAdapter mDbHelper;\r
+\r
+       public class MotorHierarchicalListAdapter extends SimpleCursorTreeAdapter\r
+       {\r
+\r
+               // Note that the constructor does not take a Cursor. This is done to avoid querying the \r
+               // database on the main thread.\r
+               public MotorHierarchicalListAdapter(Context context, Cursor cursor, int groupLayout,\r
+                               int childLayout, String[] groupFrom, int[] groupTo, String[] childrenFrom,\r
+                               int[] childrenTo) {\r
+\r
+                       super(context, cursor, groupLayout, groupFrom, groupTo, childLayout, childrenFrom,\r
+                                       childrenTo);\r
+               }\r
+\r
+               @Override\r
+               protected Cursor getChildrenCursor(Cursor arg0) {\r
+                       Log.d(TAG,"getChildrenCursor");\r
+                       String group = arg0.getString(arg0.getColumnIndex(groupColumn));\r
+                       Log.d(TAG,"  for: "+ groupColumn + " = " + group);\r
+                       Cursor c = mDbHelper.getMotorDao().fetchAllInGroups(groupColumn,group);\r
+                       Log.d(TAG,"  got cursor");\r
+                       startManagingCursor(c);\r
+                       return c;\r
+               }\r
+\r
+               @Override\r
+               public long getGroupId(int groupPosition) {\r
+                       return groupPosition;\r
+               }\r
+\r
+       }\r
+\r
+       @Override\r
+       public void onSharedPreferenceChanged(SharedPreferences arg0, String arg1) {\r
+               if ( groupColumnPreferenceKey.equals(arg1) ) {\r
+                       setGroupColumnFromPreferences(arg0);\r
+                       refreshData();\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public void onCreate(Bundle savedInstanceState) {\r
+               super.onCreate(savedInstanceState);\r
+\r
+               mDbHelper = new DbAdapter(this);\r
+               mDbHelper.open();\r
+\r
+               Resources resources = this.getResources();\r
+               groupColumnPreferenceKey = resources.getString(R.string.PreferenceMotorBrowserGroupingOption);\r
+               SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);\r
+\r
+               setGroupColumnFromPreferences(pref);\r
+\r
+               pref.registerOnSharedPreferenceChangeListener(this);\r
+\r
+               refreshData();\r
+\r
+               registerForContextMenu(getExpandableListView());\r
+\r
+       }\r
+\r
+       @Override\r
+       public boolean onCreateOptionsMenu(Menu menu) {\r
+       MenuInflater inflater = getMenuInflater();\r
+       inflater.inflate(R.menu.motor_browser_option_menu, menu);\r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       public boolean onMenuItemSelected(int featureId, MenuItem item) {\r
+               Log.d(TAG,"onMenuItemSelected" + item.getItemId());\r
+               switch(item.getItemId()) {\r
+               case R.id.download_from_thrustcurve_menu_option:\r
+                       tcDownload();\r
+                       return true;\r
+               case R.id.preference_menu_option:\r
+                       Intent intent = new Intent().setClass(this, PreferencesActivity.class);\r
+                       this.startActivity(intent);\r
+                       return true;\r
+               }\r
+               return super.onMenuItemSelected(featureId, item);\r
+       }\r
+\r
+       @Override\r
+       public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {\r
+               Log.d(TAG,"onCreateContextMenu " + menuInfo);\r
+               Log.d(TAG, "v.getId() = " + v.getId());\r
+               Log.d(TAG, "motorListView = " + R.id.motorListView);\r
+               //      if (v.getId() == R.id.motorListView) {\r
+               ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;\r
+               menu.setHeaderTitle("context menu");\r
+               menu.add(Menu.NONE,CONTEXTMENU_DELETE,CONTEXTMENU_DELETE,"Delete");\r
+               //      }\r
+               super.onCreateContextMenu(menu, v, menuInfo);\r
+       }\r
+\r
+       @Override\r
+       public boolean onContextItemSelected(MenuItem item) {\r
+               ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item.getMenuInfo();\r
+               long motorId = info.id;\r
+               Log.d(TAG,"ContextMenu: " + motorId);\r
+               switch(item.getItemId()) {\r
+               case CONTEXTMENU_DELETE:\r
+                       mDbHelper.getMotorDao().deleteMotor(motorId);\r
+                       refreshData();\r
+                       return true;\r
+               }\r
+               return super.onContextItemSelected(item);\r
+       }\r
+\r
+       @Override\r
+       protected void onActivityResult(int requestCode, int resultCode, Intent intent) {\r
+               super.onActivityResult(requestCode, resultCode, intent);\r
+               refreshData();\r
+       }\r
+\r
+\r
+       @Override\r
+       public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {\r
+               super.onChildClick(parent, v, groupPosition, childPosition, id);\r
+               Motor m = mDbHelper.getMotorDao().fetchMotor(id);\r
+               //Intent i = new Intent(this, BurnPlotActivity.class);\r
+               Intent i = new Intent(this,MotorDetails.class);\r
+               i.putExtra("Motor", m);\r
+               startActivity(i);\r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       protected void onDestroy() {\r
+               super.onDestroy();\r
+               \r
+               SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);\r
+               pref.unregisterOnSharedPreferenceChangeListener(this);\r
+\r
+               // Null out the group cursor. This will cause the group cursor and all of the child cursors\r
+               // to be closed.\r
+               mAdapter.changeCursor(null);\r
+               mAdapter = null;\r
+\r
+               mDbHelper.close();\r
+       }\r
+\r
+       private void tcDownload() {\r
+               Intent i = new Intent(this, TCQueryActivity.class);\r
+               startActivityForResult(i, ACTIVITY_DOWNLOAD);\r
+       }\r
+\r
+       private void setGroupColumnFromPreferences( SharedPreferences prefs ) {\r
+               String indexStr = prefs.getString(groupColumnPreferenceKey, "1");\r
+               int index;\r
+               //Dirty hack, you can't use integer-array in ListPreferences\r
+               try {\r
+                       index = Integer.parseInt(indexStr);\r
+               } catch ( Exception e ) {\r
+                       index = 1;\r
+               }\r
+               if ( index >= groupColumns.length ) {\r
+                       index = 1;\r
+               }\r
+               groupColumn = groupColumns[index];\r
+\r
+       }\r
+       private void refreshData() {\r
+               if (mAdapter != null ) {\r
+                       mAdapter.changeCursor(null);\r
+               }\r
+               Cursor motorCursor = mDbHelper.getMotorDao().fetchGroups(groupColumn);\r
+               startManagingCursor(motorCursor);\r
+               // Set up our adapter\r
+               mAdapter = new MotorHierarchicalListAdapter( \r
+                               this,\r
+                               motorCursor,\r
+                               R.layout.motor_list_group,\r
+                               R.layout.motor_list_child,\r
+                               new String[] { groupColumn }, // Name for group layouts\r
+                               new int[] { R.id.motorGroup },\r
+                               new String[] { MotorDao.MANUFACTURER, MotorDao.NAME, MotorDao.TOTAL_IMPULSE }, // Number for child layouts\r
+                               new int[] { R.id.motorChildManu, R.id.motorChildName, R.id.motorChildImpulse });\r
+               setListAdapter(mAdapter);\r
+       }\r
+}\r
diff --git a/src/net/sf/openrocket/android/motor/PersistentExpandableListActivity.java b/src/net/sf/openrocket/android/motor/PersistentExpandableListActivity.java
new file mode 100644 (file)
index 0000000..f179374
--- /dev/null
@@ -0,0 +1,91 @@
+package net.sf.openrocket.android.motor;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import android.app.ExpandableListActivity;\r
+import android.os.Bundle;\r
+import android.widget.ExpandableListAdapter;\r
+import android.widget.ExpandableListView;\r
+\r
+public class PersistentExpandableListActivity extends ExpandableListActivity {\r
+    private long[] expandedIds;\r
+\r
+    @Override\r
+    protected void onStart() {\r
+        super.onStart();\r
+        if (this.expandedIds != null) {\r
+            restoreExpandedState(expandedIds);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected void onStop() {\r
+        super.onStop();\r
+        expandedIds = getExpandedIds();\r
+    }\r
+\r
+    @Override\r
+    protected void onSaveInstanceState(Bundle outState) {\r
+        super.onSaveInstanceState(outState);\r
+        this.expandedIds = getExpandedIds();\r
+        outState.putLongArray("ExpandedIds", this.expandedIds);\r
+    }\r
+\r
+    @Override\r
+    protected void onRestoreInstanceState(Bundle state) {\r
+        super.onRestoreInstanceState(state);\r
+        long[] expandedIds = state.getLongArray("ExpandedIds");\r
+        if (expandedIds != null) {\r
+            restoreExpandedState(expandedIds);\r
+        }\r
+    }\r
+\r
+    private long[] getExpandedIds() {\r
+        ExpandableListView list = getExpandableListView();\r
+        ExpandableListAdapter adapter = getExpandableListAdapter();\r
+        if (adapter != null) {\r
+            int length = adapter.getGroupCount();\r
+            ArrayList<Long> expandedIds = new ArrayList<Long>();\r
+            for(int i=0; i < length; i++) {\r
+                if(list.isGroupExpanded(i)) {\r
+                    expandedIds.add(adapter.getGroupId(i));\r
+                }\r
+            }\r
+            return toLongArray(expandedIds);\r
+        } else {\r
+            return null;\r
+        }\r
+    }\r
+\r
+    private void restoreExpandedState(long[] expandedIds) {\r
+        this.expandedIds = expandedIds;\r
+        if (expandedIds != null) {\r
+            ExpandableListView list = getExpandableListView();\r
+            ExpandableListAdapter adapter = getExpandableListAdapter();\r
+            if (adapter != null) {\r
+                for (int i=0; i<adapter.getGroupCount(); i++) {\r
+                    long id = adapter.getGroupId(i);\r
+                    if (inArray(expandedIds, id)) list.expandGroup(i);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private static boolean inArray(long[] array, long element) {\r
+        for (long l : array) {\r
+            if (l == element) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private static long[] toLongArray(List<Long> list)  {\r
+        long[] ret = new long[list.size()];\r
+        int i = 0;\r
+        for (Long e : list)  \r
+            ret[i++] = e.longValue();\r
+        return ret;\r
+    }\r
+}\r
diff --git a/src/net/sf/openrocket/android/rocket/OpenRocketLoaderTask.java b/src/net/sf/openrocket/android/rocket/OpenRocketLoaderTask.java
new file mode 100644 (file)
index 0000000..aef2ace
--- /dev/null
@@ -0,0 +1,34 @@
+package net.sf.openrocket.android.rocket;\r
+\r
+import java.io.File;\r
+\r
+import net.sf.openrocket.document.OpenRocketDocument;\r
+import net.sf.openrocket.file.RocketLoadException;\r
+import net.sf.openrocket.file.openrocket.OpenRocketLoader;\r
+import android.os.AsyncTask;\r
+import android.util.Log;\r
+\r
+public class OpenRocketLoaderTask extends AsyncTask<File, Void, OpenRocketDocument> {\r
+\r
+       private final static String TAG = "OpenRocketLoaderTask";\r
+       \r
+       /* (non-Javadoc)\r
+        * @see android.os.AsyncTask#doInBackground(Params[])\r
+        */\r
+       @Override\r
+       protected OpenRocketDocument doInBackground(File... arg0) {\r
+               Log.d(TAG,"doInBackgroud");\r
+               \r
+               OpenRocketLoader rocketLoader = new OpenRocketLoader();\r
+               try {\r
+                       OpenRocketDocument rocket = rocketLoader.load(arg0[0]);\r
+                       return rocket;\r
+               }\r
+               catch( RocketLoadException ex ) {\r
+                       Log.e(TAG, "doInBackground rocketLaoder.load threw", ex);\r
+               }\r
+               return null;\r
+               \r
+       }\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java b/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java
new file mode 100644 (file)
index 0000000..35022c8
--- /dev/null
@@ -0,0 +1,143 @@
+package net.sf.openrocket.android.rocket;\r
+\r
+\r
+import java.io.File;\r
+\r
+import net.sf.openrocket.R;\r
+import net.sf.openrocket.android.Application;\r
+import net.sf.openrocket.android.PreferencesActivity;\r
+import net.sf.openrocket.android.motor.MotorHierarchicalBrowser;\r
+import net.sf.openrocket.android.simulation.SimulationViewer;\r
+import net.sf.openrocket.document.OpenRocketDocument;\r
+import net.sf.openrocket.document.Simulation;\r
+import android.app.Activity;\r
+import android.content.Intent;\r
+import android.net.Uri;\r
+import android.os.Bundle;\r
+import android.util.Log;\r
+import android.view.LayoutInflater;\r
+import android.view.Menu;\r
+import android.view.MenuInflater;\r
+import android.view.MenuItem;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.AdapterView;\r
+import android.widget.AdapterView.OnItemClickListener;\r
+import android.widget.ArrayAdapter;\r
+import android.widget.ListView;\r
+import android.widget.TextView;\r
+\r
+public class OpenRocketViewer extends Activity {\r
+\r
+       private static final String TAG = "OpenRocketViewer";\r
+\r
+       TextView header;\r
+       ListView simulationList;\r
+       \r
+       Application app;\r
+\r
+\r
+       /* (non-Javadoc)\r
+        * @see android.app.Activity#onCreate(android.os.Bundle)\r
+        */\r
+       @Override\r
+       protected void onCreate(Bundle savedInstanceState) {\r
+               super.onCreate(savedInstanceState);\r
+               \r
+               Log.d(TAG,"In onCreate");\r
+               \r
+               app = (Application) this.getApplication();\r
+\r
+               setContentView(R.layout.openrocketviewer);\r
+               \r
+               header = (TextView) findViewById(R.id.heading);\r
+               simulationList = (ListView) findViewById(R.id.rocketSimulations);\r
+\r
+               Intent i = getIntent();\r
+               Uri file = i.getData();\r
+               String path = file.getPath();\r
+               Log.d(TAG,"Use ork file: " + file);\r
+               File orkFile = new File(path);\r
+               final OpenRocketLoaderTask task = new OpenRocketLoaderTask() {\r
+\r
+                       /* (non-Javadoc)\r
+                        * @see android.os.AsyncTask#onPostExecute(java.lang.Object)\r
+                        */\r
+                       @Override\r
+                       protected void onPostExecute(OpenRocketDocument result) {\r
+                               super.onPostExecute(result);\r
+                               app.setRocketDocument( result );\r
+                               updateContents();\r
+                       }\r
+                       \r
+               };\r
+               \r
+               task.execute(orkFile);\r
+       }\r
+\r
+       private void updateContents() {\r
+               \r
+               OpenRocketDocument rocket = app.getRocketDocument();\r
+               header.setText( rocket.getRocket().getName());\r
+               \r
+               ArrayAdapter<Simulation> sims = new ArrayAdapter<Simulation>(this,android.R.layout.simple_list_item_1,rocket.getSimulations()) {\r
+\r
+                       @Override\r
+                       public View getView(int position, View convertView,\r
+                                       ViewGroup parent) {\r
+                               View v = convertView;\r
+                               if ( v == null ) {\r
+                                       LayoutInflater li = getLayoutInflater();\r
+                                       v = li.inflate(android.R.layout.simple_list_item_1,null);\r
+                               }\r
+                               Simulation sim = this.getItem(position);\r
+                               ((TextView)v.findViewById(android.R.id.text1)).setText( sim.getName() );\r
+                               return v;\r
+                       }\r
+\r
+               };\r
+               simulationList.setOnItemClickListener( new OnItemClickListener() {\r
+                       @Override\r
+                       public void onItemClick(AdapterView l, View v, int position, long id) {\r
+                               Intent i = new Intent(OpenRocketViewer.this, SimulationViewer.class);\r
+                               Log.d(TAG,"onItemClick simulation number " + id );\r
+                               i.putExtra("Simulation",(int)id);\r
+                               startActivityForResult(i, 1/*magic*/);\r
+                       }\r
+                       \r
+               });\r
+               simulationList.setAdapter(sims);\r
+\r
+       }\r
+       \r
+       @Override\r
+       public boolean onCreateOptionsMenu(Menu menu) {\r
+       MenuInflater inflater = getMenuInflater();\r
+       inflater.inflate(R.menu.rocket_viewer_option_menu, menu);\r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       public boolean onMenuItemSelected(int featureId, MenuItem item) {\r
+               Log.d(TAG,"onMenuItemSelected" + item.getItemId());\r
+               switch(item.getItemId()) {\r
+               case R.id.motor_list_menu_option:\r
+                       startMotorBrowser();\r
+                       return true;\r
+               case R.id.preference_menu_option:\r
+                       Intent intent = new Intent().setClass(this, PreferencesActivity.class);\r
+                       this.startActivity(intent);\r
+                       return true;\r
+               }\r
+               return super.onMenuItemSelected(featureId, item);\r
+       }\r
+\r
+       public void startMotorBrowser() {\r
+               Log.d(TAG,"motorBrowserButton clicked");\r
+       Intent i = new Intent(OpenRocketViewer.this, MotorHierarchicalBrowser.class);\r
+       startActivity(i);\r
+    }\r
+\r
+\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/simulation/SimulationPlotFragment.java b/src/net/sf/openrocket/android/simulation/SimulationPlotFragment.java
new file mode 100644 (file)
index 0000000..ae10b90
--- /dev/null
@@ -0,0 +1,255 @@
+package net.sf.openrocket.android.simulation;\r
+\r
+import java.util.List;\r
+import java.util.Vector;\r
+\r
+import net.sf.openrocket.R;\r
+import net.sf.openrocket.simulation.FlightDataBranch;\r
+import net.sf.openrocket.simulation.FlightDataType;\r
+import net.sf.openrocket.simulation.FlightEvent;\r
+import android.app.Activity;\r
+import android.graphics.Color;\r
+import android.graphics.PointF;\r
+import android.os.Bundle;\r
+import android.support.v4.app.Fragment;\r
+import android.util.Log;\r
+import android.view.LayoutInflater;\r
+import android.view.MotionEvent;\r
+import android.view.ScaleGestureDetector;\r
+import android.view.View;\r
+import android.view.View.OnTouchListener;\r
+import android.view.ViewGroup;\r
+\r
+import com.androidplot.xy.BoundaryMode;\r
+import com.androidplot.xy.LineAndPointFormatter;\r
+import com.androidplot.xy.LineAndPointRenderer;\r
+import com.androidplot.xy.SimpleXYSeries;\r
+import com.androidplot.xy.ValueMarker;\r
+import com.androidplot.xy.XValueMarker;\r
+import com.androidplot.xy.XYPlot;\r
+\r
+public class SimulationPlotFragment extends Fragment implements OnTouchListener {\r
+\r
+       private final static String TAG = "SimulationPlot";\r
+\r
+       private XYPlot mySimpleXYPlot;\r
+       private SimpleXYSeries mySeries;\r
+       private PointF minXY;\r
+       private PointF maxXY;\r
+       \r
+       private float absMinX;\r
+       private float absMaxX;\r
+       private float minNoError;\r
+       private float maxNoError;\r
+\r
+       private ScaleGestureDetector mScaleDetector;\r
+       private float mScaleFactor = 1.f;\r
+\r
+       @Override\r
+       public void onAttach(Activity activity) {\r
+               super.onAttach(activity);\r
+               Log.d(TAG,"onAttach");\r
+       }\r
+\r
+       @Override\r
+       public void onCreate(Bundle savedInstanceState) {\r
+               Log.d(TAG,"onCreate");\r
+               super.onCreate(savedInstanceState);\r
+       }\r
+\r
+       @Override\r
+       public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
+                       Bundle savedInstanceState) {\r
+               Log.d(TAG,"onCreateView");\r
+               View v = inflater.inflate(R.layout.motor_burn, container, false);\r
+               mySimpleXYPlot = (XYPlot) v.findViewById(R.id.xyplot);\r
+               mySimpleXYPlot.setOnTouchListener(this);\r
+               mScaleDetector = new ScaleGestureDetector(v.getContext(), new ScaleListener());\r
+               //              Motor motor = getMotor();\r
+               //              init(motor);\r
+               return v;\r
+       }\r
+\r
+       void init( FlightDataBranch data, FlightDataType selectedSeries, List<FlightEvent> eventsToShow ) {\r
+\r
+               mySimpleXYPlot.clear();\r
+               \r
+               if ( data == null || selectedSeries == null || eventsToShow == null ) {\r
+                       return;\r
+               }\r
+               \r
+               mySimpleXYPlot.setUserDomainOrigin(0);\r
+               mySimpleXYPlot.setUserRangeOrigin(0);\r
+               mySimpleXYPlot.setRangeLabel("");\r
+               mySimpleXYPlot.setDomainLabel(FlightDataType.TYPE_TIME.getUnitGroup().getDefaultUnit().toString());\r
+               mySimpleXYPlot.setRangeLabel( selectedSeries.getUnitGroup().getDefaultUnit().toString() ); \r
+               mySimpleXYPlot.disableAllMarkup();\r
+\r
+               for ( FlightEvent event : eventsToShow ) {\r
+                       XValueMarker xmarker = new XValueMarker( event.getTime(), event.getType().toString() );\r
+                       xmarker.setTextOrientation( ValueMarker.TextOrientation.VERTICAL );\r
+                       mySimpleXYPlot.addMarker( xmarker );\r
+               }\r
+               \r
+               List<Double> yvals = null;\r
+               List<Double> xvals = null;\r
+               try {\r
+                       yvals = data.get(selectedSeries);\r
+                       xvals = data.get(FlightDataType.TYPE_TIME);\r
+                       Log.d("plot","data = " + yvals);\r
+               } catch ( Exception ex ) {\r
+                       Log.d(TAG, "Exception: " + ex);\r
+               }\r
+               if ( yvals == null || yvals.size() == 0 ) {\r
+                       yvals = new Vector<Double>();\r
+                       yvals.add(0.0);\r
+                       yvals.add(0.0);\r
+                       yvals.add(1.0);\r
+                       yvals.add(1.0);\r
+               }\r
+               Log.d("plot","data = " + yvals.toString());\r
+\r
+               mySeries = new SimpleXYSeries(xvals, yvals, FlightDataType.TYPE_ALTITUDE.toString());\r
+\r
+               mySimpleXYPlot.addSeries(mySeries, LineAndPointRenderer.class,\r
+                               new LineAndPointFormatter(Color.rgb(0, 255, 0), Color.rgb(200, 0, 0), null));\r
+\r
+               //Set of internal variables for keeping track of the boundaries\r
+               mySimpleXYPlot.calculateMinMaxVals();\r
+       \r
+               mySimpleXYPlot.redraw();\r
+\r
+               minXY=new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),mySimpleXYPlot.getCalculatedMinY().floatValue());\r
+               maxXY=new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),mySimpleXYPlot.getCalculatedMaxY().floatValue());\r
+\r
+               absMinX = minXY.x;\r
+               absMaxX = maxXY.x;\r
+               \r
+               minNoError = Math.round(mySeries.getX(1).floatValue() +2);\r
+               maxNoError = Math.round(mySeries.getX(mySeries.size() -1).floatValue()) - 2.0f;\r
+       }\r
+\r
+       private float mPosX;\r
+       private float mPosY;\r
+\r
+       private float mLastTouchX;\r
+       private float mLastTouchY;\r
+\r
+       private int mActivePointerId = -1;\r
+\r
+       @Override\r
+       public boolean onTouch(View arg0, MotionEvent event) {\r
+               mScaleDetector.onTouchEvent(event);\r
+\r
+               final int action = event.getAction();\r
+               switch ( action & MotionEvent.ACTION_MASK ) {\r
+               case MotionEvent.ACTION_DOWN: {\r
+                       final float x = event.getX();\r
+                       final float y = event.getY();\r
+\r
+                       mLastTouchX = x;\r
+                       mLastTouchY = y;\r
+\r
+                       mActivePointerId = event.getPointerId(0);\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_MOVE: {\r
+                       final int pointerIndex = event.findPointerIndex(mActivePointerId);\r
+                       final float x = event.getX(pointerIndex);\r
+                       final float y = event.getY(pointerIndex);\r
+\r
+                       if (!mScaleDetector.isInProgress()) {\r
+                               final float dx = x - mLastTouchX;\r
+                               final float dy = y - mLastTouchY;\r
+                       \r
+                               mPosX += dx;\r
+                               mPosY += dy;\r
+                               scroll(dx);\r
+                               // do scroll.\r
+                       \r
+                       }\r
+                       mLastTouchX = x;\r
+                       mLastTouchY = y;\r
+                       \r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_UP: {\r
+                       mActivePointerId = -1;\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_CANCEL: {\r
+                       mActivePointerId = -1;\r
+                       break;\r
+               }\r
+               \r
+               case MotionEvent.ACTION_POINTER_UP: {\r
+                       final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;\r
+                       final int pointerId = event.getPointerId(pointerIndex);\r
+                       if (pointerId == mActivePointerId) {\r
+                               // This was our active pointer going up.  choose a new active pointer and adjust accordingly.\r
+                               final int newPointerIndex = pointerIndex ==0 ? 1:0;\r
+                               mLastTouchX = event.getX(newPointerIndex);\r
+                               mLastTouchY = event.getY(newPointerIndex);\r
+                               mActivePointerId = event.getPointerId(newPointerIndex);\r
+                       }\r
+                       break;\r
+               }\r
+               }       \r
+               return true;\r
+       }\r
+\r
+       private void zoom(float scale) {\r
+               Log.d(TAG,"zoom by " + scale);\r
+               float domainSpan = absMaxX      - absMinX;\r
+               Log.d(TAG,"domainSpan = " + domainSpan);\r
+               float domainMidPoint = absMaxX          - domainSpan / 2.0f;\r
+               Log.d(TAG,"domainMidPoint = " + domainMidPoint);\r
+               float offset = domainSpan / scale;\r
+               Log.d(TAG,"offset " + offset);\r
+               minXY.x=domainMidPoint- offset;\r
+               Log.d(TAG,"min X " + minXY.x);\r
+               maxXY.x=domainMidPoint+offset;\r
+               Log.d(TAG,"max X " + maxXY.x);\r
+               checkBoundaries();\r
+               mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);\r
+               mySimpleXYPlot.redraw();\r
+       }\r
+\r
+       private void scroll(float pan) {\r
+               float domainSpan = maxXY.x      - minXY.x;\r
+               float step = domainSpan / mySimpleXYPlot.getWidth();\r
+               float offset = pan * step;\r
+               minXY.x+= offset;\r
+               maxXY.x+= offset;\r
+               checkBoundaries();\r
+               mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);\r
+               mySimpleXYPlot.redraw();\r
+       }\r
+\r
+       private void checkBoundaries() {\r
+               \r
+               if ( minXY.x < absMinX) \r
+                       minXY.x = absMinX;\r
+//             else if ( minXY.x > maxNoError )\r
+//                     minXY.x = maxNoError;\r
+               \r
+               if ( maxXY.x > absMaxX)\r
+                       maxXY.x = absMaxX;\r
+//             else if ( maxXY.x < minNoError)\r
+//                     maxXY.x = minNoError;\r
+       }\r
+       private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {\r
+               @Override\r
+               public boolean onScale( ScaleGestureDetector detector ) {\r
+                       mScaleFactor *= detector.getScaleFactor();\r
+\r
+                       mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));\r
+                       zoom(mScaleFactor);\r
+                       return true;\r
+               }\r
+       }\r
+}\r
+\r
diff --git a/src/net/sf/openrocket/android/simulation/SimulationViewer.java b/src/net/sf/openrocket/android/simulation/SimulationViewer.java
new file mode 100644 (file)
index 0000000..fd4bd5f
--- /dev/null
@@ -0,0 +1,187 @@
+package net.sf.openrocket.android.simulation;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import net.sf.openrocket.R;\r
+import net.sf.openrocket.android.Application;\r
+import net.sf.openrocket.document.Simulation;\r
+import net.sf.openrocket.simulation.FlightDataBranch;\r
+import net.sf.openrocket.simulation.FlightDataType;\r
+import net.sf.openrocket.simulation.FlightEvent;\r
+import android.content.Intent;\r
+import android.os.Bundle;\r
+import android.support.v4.app.FragmentActivity;\r
+import android.util.Log;\r
+import android.util.SparseBooleanArray;\r
+import android.view.LayoutInflater;\r
+import android.view.Menu;\r
+import android.view.MenuInflater;\r
+import android.view.MenuItem;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.ArrayAdapter;\r
+import android.widget.ImageView;\r
+import android.widget.ListView;\r
+import android.widget.SlidingDrawer;\r
+import android.widget.TabHost;\r
+import android.widget.TextView;\r
+\r
+public class SimulationViewer extends FragmentActivity\r
+implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListener {\r
+\r
+       private final static String TAG = "MotorDetails";\r
+\r
+       private SlidingDrawer slidingDrawer;\r
+       private ImageView handle;\r
+\r
+       private ListView eventList;\r
+       private ListView seriesList;\r
+\r
+       private SimulationPlotFragment simPlot;\r
+\r
+       private Simulation sim;\r
+       private FlightDataBranch data;\r
+\r
+       @Override\r
+       public void onCreate(Bundle savedInstanceState) {\r
+               super.onCreate(savedInstanceState);\r
+               Log.d(TAG,"onCreate Bundle = "+ String.valueOf(savedInstanceState));\r
+               setContentView(R.layout.simulation_detail);\r
+\r
+               Intent i = getIntent();\r
+               int simnumber = i.getIntExtra("Simulation", 0);\r
+               sim = ((Application)this.getApplication()).getRocketDocument().getSimulation(simnumber);\r
+               data = sim.getSimulatedData().getBranch(0);\r
+\r
+               simPlot = (SimulationPlotFragment) getSupportFragmentManager().findFragmentById(R.id.simulationPlotFragment);\r
+\r
+               slidingDrawer = (SlidingDrawer) findViewById(R.id.drawer);\r
+\r
+               slidingDrawer.setOnDrawerOpenListener(this);\r
+               slidingDrawer.setOnDrawerCloseListener(this);\r
+\r
+               handle = (ImageView) findViewById(R.id.handle);\r
+\r
+               TabHost tabs=(TabHost)findViewById(R.id.simulationConfigurationForm);\r
+\r
+               tabs.setup();\r
+\r
+               TabHost.TabSpec spec=tabs.newTabSpec("tag1");\r
+\r
+               spec.setContent(R.id.simulationEventsList);\r
+               spec.setIndicator("Events");\r
+               tabs.addTab(spec);\r
+\r
+               spec=tabs.newTabSpec("tag2");\r
+               spec.setContent(R.id.simulationSeriesList);\r
+               spec.setIndicator("Series");\r
+               tabs.addTab(spec);      \r
+\r
+               eventList = (ListView) findViewById(R.id.simulationEventsList);\r
+\r
+               seriesList = (ListView) findViewById(R.id.simulationSeriesList);\r
+\r
+               // Initialize the eventList\r
+               ArrayAdapter<FlightEvent> events = new ArrayAdapter<FlightEvent>(this,android.R.layout.simple_list_item_multiple_choice,data.getEvents()) {\r
+\r
+                       @Override\r
+                       public View getView(int position, View convertView,\r
+                                       ViewGroup parent) {\r
+                               View v = convertView;\r
+                               if ( v == null ) {\r
+                                       LayoutInflater li = getLayoutInflater();\r
+                                       v = li.inflate(android.R.layout.simple_list_item_multiple_choice,null);\r
+                               }\r
+                               FlightEvent event = this.getItem(position);\r
+                               ((TextView)v.findViewById(android.R.id.text1)).setText( event.getType().toString() + " " + event.getTime() + " (s)" );\r
+                               return v;\r
+                       }\r
+\r
+               };\r
+               eventList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);\r
+               eventList.setAdapter(events);\r
+\r
+               List<FlightDataType> selectableSeries = new ArrayList<FlightDataType>();\r
+               for( FlightDataType fdt : data.getTypes() ) {\r
+                       if ( fdt == FlightDataType.TYPE_TIME ) { \r
+\r
+                       } else {\r
+                               selectableSeries.add(fdt);\r
+                       }\r
+               }\r
+               ArrayAdapter<FlightDataType> serieses = new ArrayAdapter<FlightDataType>(this,android.R.layout.simple_list_item_multiple_choice,selectableSeries) {\r
+\r
+                       @Override\r
+                       public View getView(int position, View convertView,\r
+                                       ViewGroup parent) {\r
+                               View v = convertView;\r
+                               if ( v == null ) {\r
+                                       LayoutInflater li = getLayoutInflater();\r
+                                       v = li.inflate(android.R.layout.simple_list_item_multiple_choice,null);\r
+                               }\r
+                               FlightDataType fdt = this.getItem(position);\r
+                               ((TextView)v.findViewById(android.R.id.text1)).setText( fdt.toString() );\r
+                               return v;\r
+                       }\r
+\r
+               };\r
+               seriesList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);\r
+               seriesList.setAdapter(serieses);\r
+               redraw();\r
+\r
+       }\r
+\r
+       @Override\r
+       public void onDrawerOpened() {\r
+               handle.setImageResource(R.drawable.arrow_down_float);\r
+       }\r
+\r
+       @Override\r
+       public void onDrawerClosed() {\r
+               handle.setImageResource(R.drawable.arrow_up_float);\r
+               redraw();\r
+       }\r
+\r
+       @Override\r
+       public boolean onCreateOptionsMenu(Menu menu) {\r
+               MenuInflater inflater = getMenuInflater();\r
+               inflater.inflate(R.menu.motor_details_option_menu, menu);\r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       public boolean onMenuItemSelected(int featureId, MenuItem item) {\r
+               switch(item.getItemId()) {\r
+               case R.id.save:\r
+                       // Extract form data to Motor.\r
+                       // Save motor.\r
+                       return true;\r
+               }\r
+               return super.onMenuItemSelected(featureId, item);\r
+       }\r
+\r
+       private void redraw() {\r
+               List<FlightEvent> eventsToShow = new ArrayList<FlightEvent>();\r
+               {\r
+                       SparseBooleanArray eventsSelected = eventList.getCheckedItemPositions();\r
+                       List<FlightEvent> flightEvents = data.getEvents();\r
+                       for( int i=0; i< flightEvents.size(); i++ ) {\r
+                               if ( eventsSelected.get(i) ) {\r
+                                       eventsToShow.add(flightEvents.get(i) );\r
+                               }\r
+                       }\r
+               }\r
+               FlightDataType selectedSeries = null;\r
+               {\r
+                       int selected = seriesList.getCheckedItemPosition();\r
+                       if ( selected >= 0 ) {\r
+                               selectedSeries = (FlightDataType) seriesList.getAdapter().getItem(selected);\r
+                       }\r
+               }\r
+\r
+               simPlot.init(data, selectedSeries, eventsToShow );\r
+\r
+       }\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/Base64Decoder.java b/src/net/sf/openrocket/android/thrustcurve/Base64Decoder.java
new file mode 100644 (file)
index 0000000..e172edc
--- /dev/null
@@ -0,0 +1,122 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.IOException;\r
+import java.io.Reader;\r
+import java.io.StringReader;\r
+import java.io.StringWriter;\r
+\r
+public abstract class Base64Decoder {\r
+\r
+       private static final String BASE64_CHARS =\r
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+       private static final char PAD_CHAR = '=';\r
+\r
+       private final static short[]  _charToBits = new short[128];\r
+\r
+       static {\r
+\r
+               for (int i = 0; i < _charToBits.length; i++)\r
+                       _charToBits[i] = -1;\r
+\r
+               for (int i = 0; i < BASE64_CHARS.length(); i++)\r
+                       _charToBits[BASE64_CHARS.charAt(i)] = (byte) i;\r
+               _charToBits[PAD_CHAR] = 0;\r
+\r
+       }\r
+       \r
+       /**\r
+        * Decode the specified Base64 string and write binary data\r
+        * to the given stream.\r
+        * @param str Base64 encoded string\r
+        * @param w output stream\r
+        */\r
+       public static String decodeData(String str) throws IOException\r
+       {\r
+               StringReader  r;\r
+               int           c1;\r
+\r
+               if (str == null || str.length() < 1)\r
+                       return null;\r
+\r
+               r = new StringReader(str);\r
+               \r
+               StringWriter w = new StringWriter();\r
+\r
+               // spin through the input string\r
+               c1 = readToNonSpace(r);\r
+               while (c1 > 0)\r
+               {\r
+                       int  c2, c3, c4;\r
+                       int  p1, p2, p3, p4;\r
+                       int  pad, n;\r
+\r
+                       pad = 0;\r
+\r
+                       c2 = readToNonSpace(r);\r
+                       c3 = readToNonSpace(r);\r
+                       c4 = readToNonSpace(r);\r
+                       if (c4 < 0)\r
+                               throw new IllegalArgumentException("Encoded string ends prematurely.");\r
+\r
+                       p1 = charToBits(c1);\r
+                       p2 = charToBits(c2);\r
+\r
+                       if (c3 == PAD_CHAR)\r
+                       {\r
+                               p3 = 0;\r
+                               pad++;\r
+                       }\r
+                       else\r
+                               p3 = charToBits(c3);\r
+\r
+                       if (c4 == PAD_CHAR)\r
+                       {\r
+                               p4 = 0;\r
+                               pad++;\r
+                       }\r
+                       else\r
+                               p4 = charToBits(c4);\r
+\r
+                       if (p1 < 0 || p2 < 0 || p3 < 0 || p4 < 0)\r
+                               throw new IllegalArgumentException("Encoded string contains invalid characters.");\r
+\r
+                       n = (p1 << 18) | (p2 << 12) | (p3 << 6) | p4;\r
+\r
+                       w.write((byte) ((n & 0xFF0000) >> 16));\r
+                       if (pad < 2)\r
+                               w.write((byte) ((n & 0x00FF00) >> 8));\r
+                       if (pad < 1)\r
+                               w.write((byte) (n & 0x0000FF));\r
+\r
+                       c1 = readToNonSpace(r);\r
+                       if (c1 > 0 && pad > 0)\r
+                               throw new IllegalArgumentException("Extra characters found after padding.");\r
+               }\r
+               \r
+               return w.toString();\r
+       }\r
+\r
+\r
+       private static int readToNonSpace(Reader r)\r
+       throws IOException\r
+       {\r
+               int  c;\r
+\r
+               c = r.read();\r
+               while (c >= 0 && Character.isWhitespace(c))\r
+                       c = r.read();\r
+\r
+               return c;\r
+       }\r
+\r
+       private static int charToBits(int c)\r
+       {\r
+               // use it to look up the value\r
+               if (c < 0 || c >= _charToBits.length)\r
+                       return -1;\r
+               else\r
+                       return _charToBits[c];\r
+       }\r
+\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/DownloadRequest.java b/src/net/sf/openrocket/android/thrustcurve/DownloadRequest.java
new file mode 100644 (file)
index 0000000..98d22c1
--- /dev/null
@@ -0,0 +1,43 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.ArrayList;\r
+\r
+class DownloadRequest {\r
+\r
+       private ArrayList<Integer> motorIds = new ArrayList<Integer>();\r
+       \r
+       private String format = null;\r
+       \r
+       public void add( Integer motorId ) {\r
+               this.motorIds.add(motorId);\r
+       }\r
+       \r
+       public void setFormat( String format ) {\r
+               this.format = format;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder w = new StringBuilder();\r
+               \r
+        w.append("<?xml version=\"1.0\" encoding=\"ascii\"?>\n");\r
+        w.append("<download-request\n");\r
+        w.append(" xmlns=\"http://www.thrustcurve.org/2008/DownloadRequest\"\n");\r
+        w.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");\r
+        w.append(" xsi:schemaLocation=\"http://www.thrustcurve.org/2008/DownloadRequest http://www.thrustcurve.org/2008/download-request.xsd\">\n");\r
+\r
+        if ( format != null ) {\r
+               w.append("  <format>").append(format).append("</format>\n");\r
+        }\r
+        \r
+        w.append("  <motor-ids>\n");\r
+          for( Integer i : motorIds ) {\r
+                 w.append("      <id>").append(i).append("</id>\n");\r
+          }\r
+        w.append("  </motor-ids>\n");\r
+        w.append("</download-request>\n");\r
+        return w.toString();\r
+       }\r
+\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/DownloadResponse.java b/src/net/sf/openrocket/android/thrustcurve/DownloadResponse.java
new file mode 100644 (file)
index 0000000..84e1591
--- /dev/null
@@ -0,0 +1,37 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+\r
+public class DownloadResponse {\r
+\r
+       private Map<Integer,MotorBurnFile> data = new HashMap<Integer,MotorBurnFile>();\r
+       \r
+       private String error = null;\r
+       \r
+       public void add( MotorBurnFile mbd ) {\r
+               MotorBurnFile currentData = data.get(mbd.getMotorId());\r
+               if ( currentData == null || currentData.getDatapoints().size() < mbd.getDatapoints().size() ) {\r
+                       data.put(mbd.getMotorId(),mbd);\r
+               }\r
+       }\r
+\r
+       public MotorBurnFile getData(Integer motor_id) {\r
+               return data.get(motor_id);\r
+       }\r
+       \r
+       public void setError(String error) {\r
+               this.error = error;\r
+       }\r
+       \r
+       public String getError() {\r
+               return error;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               return "DownloadResponse [error=" + error + ", data=" + data + "]";\r
+       }\r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/DownloadResponseParser.java b/src/net/sf/openrocket/android/thrustcurve/DownloadResponseParser.java
new file mode 100644 (file)
index 0000000..7e24e7e
--- /dev/null
@@ -0,0 +1,122 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.InputStream;\r
+\r
+import org.xml.sax.Attributes;\r
+\r
+import android.sax.Element;\r
+import android.sax.EndElementListener;\r
+import android.sax.EndTextElementListener;\r
+import android.sax.RootElement;\r
+import android.sax.StartElementListener;\r
+import android.util.Log;\r
+import android.util.Xml;\r
+\r
+public class DownloadResponseParser {\r
+\r
+       private static final String TAG = "DownloadResponseParser";\r
+\r
+       private static final String thrustcurveURI = "http://www.thrustcurve.org/2009/DownloadResponse";\r
+\r
+       private static final String root_tag = "download-response";\r
+       private static final String results_tag = "results";\r
+       private static final String result_tag = "result";\r
+       private static final String motor_id_tag = "motor-id";\r
+       private static final String simfile_id_tag = "simfile-id";\r
+       private static final String format_tag = "format";\r
+       private static final String source_tag = "source";\r
+       private static final String license_tag = "license";\r
+       private static final String data_tag = "data";\r
+       private static final String error_tag = "error";\r
+\r
+       public static DownloadResponse parse( InputStream in ) {\r
+\r
+               final DownloadResponse ret = new DownloadResponse();\r
+               final MotorBurnFile currentMotor = new MotorBurnFile();\r
+\r
+               // Have a place to put the data string and format.\r
+               // We hold on to these here, then push them into the currentMotor\r
+               // only if it a supported filetype\r
+               final StringHolder current_format = new StringHolder();\r
+               final StringHolder current_data = new StringHolder();\r
+\r
+               RootElement rootEl = new RootElement(thrustcurveURI, root_tag);\r
+               /*\r
+               rootEl.setStartElementListener(\r
+                               new StartElementListener() {\r
+                                       public void start(Attributes arg0) {\r
+                                               Log.d(TAG,"Start Element error");\r
+                                               ret.setError("IsError");\r
+                                       }\r
+                               }\r
+                               );\r
+                               */\r
+               Element resultsEl = rootEl.getChild( thrustcurveURI, results_tag);\r
+               Element resultEl = resultsEl.getChild( thrustcurveURI, result_tag);\r
+               resultEl.setStartElementListener(\r
+                               new StartElementListener() {\r
+                                       @Override\r
+                                       public void start(Attributes arg0) {\r
+                                               Log.d(TAG,"Start Element result");\r
+                                               currentMotor.init();\r
+                                       }\r
+                               }\r
+                               );\r
+\r
+               resultEl.setEndElementListener(\r
+                               new EndElementListener() {\r
+                                       @Override\r
+                                       public void end() {\r
+                                               if ( SupportedFileTypes.isSupportedFileType(current_format.s) ) {\r
+                                                       currentMotor.setFiletype(current_format.s);\r
+                                                       String s = null;\r
+                                                       try {\r
+                                                               s = Base64Decoder.decodeData(current_data.s);\r
+                                                       } catch ( Exception ex ) {\r
+                                                               Log.d(TAG,"base64: " + ex.getMessage());\r
+                                                       }\r
+                                                       currentMotor.decodeFile( s );\r
+                                               }\r
+                                               ret.add((MotorBurnFile)currentMotor.clone());\r
+                                       }\r
+                               }\r
+                               );\r
+\r
+               resultEl.getChild(thrustcurveURI,motor_id_tag).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setMotor_id(Integer.parseInt(arg0));\r
+                                       }\r
+                               }\r
+                               );\r
+               resultEl.getChild(thrustcurveURI,format_tag).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               current_format.s = arg0;\r
+                                       }\r
+                               }\r
+                               );\r
+               resultEl.getChild(thrustcurveURI,data_tag).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               current_data.s = arg0;\r
+                                       }\r
+                               }\r
+                               );\r
+               try {\r
+                       Xml.parse(in, Xml.Encoding.UTF_8,  rootEl.getContentHandler());\r
+               } catch (Exception e) {\r
+                       throw new RuntimeException(e);\r
+               }\r
+\r
+               return ret;\r
+       }\r
+\r
+       private static class StringHolder {\r
+               public String s;\r
+       }\r
+\r
+}
\ No newline at end of file
diff --git a/src/net/sf/openrocket/android/thrustcurve/MotorBurnFile.java b/src/net/sf/openrocket/android/thrustcurve/MotorBurnFile.java
new file mode 100644 (file)
index 0000000..58f7bb3
--- /dev/null
@@ -0,0 +1,89 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.Vector;\r
+\r
+public class MotorBurnFile {\r
+\r
+       private Integer motor_id;\r
+       private String filetype;\r
+       private Float length;\r
+       private String delays;\r
+       private Double propWeightG;\r
+       private Double totWeightG;\r
+       private Vector<Double> datapoints = new Vector<Double>();\r
+       \r
+       public void init() {\r
+               this.motor_id = null;\r
+               this.filetype = null;\r
+               this.length = null;\r
+               this.delays = null;\r
+               this.propWeightG = null;\r
+               this.totWeightG = null;\r
+               this.datapoints = new Vector<Double>();\r
+       }\r
+       \r
+       @Override\r
+       public MotorBurnFile clone() {\r
+               MotorBurnFile clone = new MotorBurnFile();\r
+               clone.motor_id = this.motor_id;\r
+               clone.filetype = this.filetype;\r
+               clone.length = this.length;\r
+               clone.delays = this.delays;\r
+               clone.propWeightG = this.propWeightG;\r
+               clone.totWeightG = this.totWeightG;\r
+               clone.datapoints = this.datapoints;\r
+               return clone;\r
+       }\r
+\r
+       public void decodeFile(String data){\r
+               if (SupportedFileTypes.RASP_FORMAT.equals(filetype)) {\r
+                       RaspBurnFile.parse(this,data);\r
+               } else if (SupportedFileTypes.ROCKSIM_FORMAT.equals(filetype) ){\r
+                       RSEBurnFile.parse(this,data);\r
+               }\r
+       }\r
+       \r
+       public Integer getMotorId() {\r
+               return motor_id;\r
+       }\r
+       public String getFileType() {\r
+               return filetype;\r
+       }\r
+       public Float getLength() {\r
+               return length;\r
+       }\r
+       public String getDelays() {\r
+               return delays;\r
+       }\r
+       public Double getPropWeightG() {\r
+               return propWeightG;\r
+       }\r
+       public Double getTotWeightG() {\r
+               return totWeightG;\r
+       }\r
+       public Vector<Double> getDatapoints() {\r
+               return datapoints;\r
+       }\r
+\r
+       void setMotor_id(Integer motor_id) {\r
+               this.motor_id = motor_id;\r
+       }\r
+       void setFiletype(String filetype ) {\r
+               this.filetype = filetype;\r
+       }\r
+       void setLength(Float length) {\r
+               this.length = length;\r
+       }\r
+       void setDelays(String delays) {\r
+               this.delays = delays;\r
+       }\r
+       void setPropWeightG(Double propWeightG) {\r
+               this.propWeightG = propWeightG;\r
+       }\r
+       void setTotWeightG(Double totWeightG) {\r
+               this.totWeightG = totWeightG;\r
+       }\r
+       void setDatapoints(Vector<Double> datapoints) {\r
+               this.datapoints = datapoints;\r
+       }\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/RSEBurnFile.java b/src/net/sf/openrocket/android/thrustcurve/RSEBurnFile.java
new file mode 100644 (file)
index 0000000..0c93757
--- /dev/null
@@ -0,0 +1,131 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.InputStream;\r
+import java.util.Vector;\r
+\r
+import org.xml.sax.Attributes;\r
+\r
+import android.sax.Element;\r
+import android.sax.RootElement;\r
+import android.sax.StartElementListener;\r
+import android.util.Log;\r
+import android.util.Xml;\r
+\r
+class RSEBurnFile extends MotorBurnFile {\r
+\r
+       private final static String TAG = "RSEBurnFile";\r
+\r
+       static void parse( MotorBurnFile that, String filecontents ) {\r
+\r
+               parse(that, new ByteArrayInputStream(filecontents.getBytes()) );\r
+       }\r
+\r
+       private final static String root_tag = "engine-database";\r
+       private final static String engine_list_tag = "engine-list";\r
+       private final static String engine_tag = "engine";\r
+\r
+       private final static String delays_attr = "delays";\r
+       private final static String len_attr = "len";\r
+       private final static String propwgt_attr = "propWt";\r
+       private final static String totwgt_attr = "initWt";\r
+\r
+       private final static String data_tag = "data";\r
+       private final static String eng_data_tag = "eng-data";\r
+\r
+       private final static String time_attr="t";\r
+       private final static String force_attr="f";\r
+\r
+       static void parse( final MotorBurnFile that, InputStream in ) {\r
+\r
+               RootElement rootEl = new RootElement(root_tag);\r
+               Element engineEl = rootEl.getChild(engine_list_tag).getChild(engine_tag);\r
+\r
+               final Vector<Double> datapoints = new Vector<Double>();\r
+               \r
+               Log.d(TAG,"parsing start");\r
+\r
+               engineEl.setStartElementListener(\r
+                               new StartElementListener() {\r
+                                       @Override\r
+                                       public void start(Attributes arg0) {\r
+                                               Log.d(TAG,"start engineEl");\r
+                                               that.setPropWeightG(Double.parseDouble(arg0.getValue(propwgt_attr)));\r
+                                               that.setTotWeightG(Double.parseDouble(arg0.getValue(totwgt_attr)));\r
+                                               that.setLength(Float.parseFloat(arg0.getValue(len_attr)));\r
+                                               that.setDelays(arg0.getValue(delays_attr));\r
+                                               Log.d(TAG, "me is now " + that.toString());\r
+                                       }\r
+                               }\r
+               );\r
+\r
+               Element datapointEl = engineEl.getChild(data_tag).getChild(eng_data_tag);\r
+               datapointEl.setStartElementListener(\r
+                               new StartElementListener() {\r
+                                       @Override\r
+                                       public void start(Attributes attributes) {\r
+                                               Double x = Double.parseDouble(attributes.getValue(time_attr));\r
+                                               Double y = Double.parseDouble(attributes.getValue(force_attr));\r
+                                               Log.d(TAG, "add data point " + x + "," + y);\r
+                                               datapoints.add(x);\r
+                                               datapoints.add(y);\r
+                                       }\r
+                               }\r
+               );\r
+\r
+               try {\r
+            Xml.parse(in, Xml.Encoding.UTF_8,  rootEl.getContentHandler());\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+\r
+               that.setDatapoints(datapoints);\r
+       }\r
+}\r
+//\r
+//     <engine-database>\r
+//      <engine-list>\r
+//     <engine FDiv="10" FFix="1" FStep="-1." Isp="202.11" Itot="8.919" Type="single-use" auto-calc-cg="1" auto-calc-mass="1" avgThrust="3.795" burn-time="2.35" cgDiv="10" cgFix="1" cgStep="-1." code="C4" delays="3,5,7" dia="18." exitDia="0." initWt="17." len="50." mDiv="10" mFix="1" mStep="-1." massFrac="26.47" mfg="Apogee" peakThrust="11.31" propWt="4.5" tDiv="10" tFix="1" tStep="-1." throatDia="0.">\r
+//     <comments>Apogee C4 RASP.ENG file made from NAR published data\r
+//     File produced September 4, 2000\r
+//     The total impulse, peak thrust, average thrust and burn time are\r
+//     the same as the averaged static test data on the NAR web site in\r
+//     the certification file. The curve drawn with these data points is as\r
+//     close to the certification curve as can be with such a limited\r
+//     number of points (32) allowed with wRASP up to v1.6.\r
+//     </comments>\r
+//     <data>\r
+//     <eng-data cg="25." f="0." m="4.5" t="0."/>\r
+//     <eng-data cg="25." f="3.23" m="4.48533" t="0.018"/>\r
+//     <eng-data cg="25." f="6.874" m="4.42671" t="0.041"/>\r
+//     <eng-data cg="25." f="8.779" m="4.00814" t="0.147"/>\r
+//     <eng-data cg="25." f="10.683" m="3.28643" t="0.294"/>\r
+//     <eng-data cg="25." f="11.31" m="2.89252" t="0.365"/>\r
+//     <eng-data cg="25." f="10.521" m="2.76585" t="0.388"/>\r
+//     <eng-data cg="25." f="8.779" m="2.649" t="0.412"/>\r
+//     <eng-data cg="25." f="7.04" m="2.53328" t="0.441"/>\r
+//     <eng-data cg="25." f="4.555" m="2.46308" t="0.465"/>\r
+//     <eng-data cg="25." f="3.479" m="2.33337" t="0.529"/>\r
+//     <eng-data cg="25." f="2.981" m="2.1704" t="0.629"/>\r
+//     <eng-data cg="25." f="3.23" m="2.1328" t="0.653"/>\r
+//     <eng-data cg="25." f="2.816" m="2.03366" t="0.718"/>\r
+//     <eng-data cg="25." f="2.733" m="1.84469" t="0.853"/>\r
+//     <eng-data cg="25." f="2.65" m="1.5568" t="1.065"/>\r
+//     <eng-data cg="25." f="2.567" m="1.30938" t="1.253"/>\r
+//     <eng-data cg="25." f="2.401" m="1.05873" t="1.453"/>\r
+//     <eng-data cg="25." f="2.484" m="0.761739" t="1.694"/>\r
+//     <eng-data cg="25." f="2.484" m="0.636413" t="1.794"/>\r
+//     <eng-data cg="25." f="2.733" m="0.612724" t="1.812"/>\r
+//     <eng-data cg="25." f="2.401" m="0.575165" t="1.841"/>\r
+//     <eng-data cg="25." f="2.401" m="0.446759" t="1.947"/>\r
+//     <eng-data cg="25." f="2.401" m="0.246881" t="2.112"/>\r
+//     <eng-data cg="25." f="2.401" m="0.0978809" t="2.235"/>\r
+//     <eng-data cg="25." f="2.236" m="0.0429024" t="2.282"/>\r
+//     <eng-data cg="25." f="1.656" m="0.0134478" t="2.312"/>\r
+//     <eng-data cg="25." f="0.662" m="0.003507" t="2.329"/>\r
+//     <eng-data cg="25." f="0." m="-0." t="2.35"/>\r
+//     </data>\r
+//     </engine>\r
+//      </engine-list>\r
+//     </engine-database>\r
+//\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/RaspBurnFile.java b/src/net/sf/openrocket/android/thrustcurve/RaspBurnFile.java
new file mode 100644 (file)
index 0000000..2c2e834
--- /dev/null
@@ -0,0 +1,85 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.IOException;\r
+import java.io.LineNumberReader;\r
+import java.io.StringReader;\r
+import java.util.Vector;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import android.util.Log;\r
+\r
+\r
+class RaspBurnFile{\r
+\r
+       private final static String TAG = "RaspBurnFile";\r
+       \r
+       private final static int HEADER = 0;\r
+       private final static int DATA = 1;\r
+       private final static Pattern headerPattern = Pattern.compile("(\\S*)\\s+(\\S*)\\s+(\\S*)\\s+(\\S*)\\s+(\\S*)\\s+(\\S*)\\s+(\\S*)");\r
+       private final static Pattern dataPattern = Pattern.compile("(\\S*)\\s+(\\S*)");\r
+       \r
+       static void parse( MotorBurnFile that, String filecontents ) {\r
+               \r
+               int state = HEADER;\r
+               \r
+               LineNumberReader reader = new LineNumberReader( new StringReader(filecontents));\r
+               \r
+               Vector<Double> datapoints = new Vector<Double>();\r
+               \r
+               String line;\r
+               Matcher m;\r
+               try {\r
+               while ( (line = reader.readLine()) != null ) {\r
+                       line = line.trim();\r
+                       Log.d("RASP",line);\r
+                       if ( line.startsWith(";")) {\r
+                               continue;\r
+                       }\r
+                       switch (state) {\r
+                       \r
+                       case HEADER:\r
+                               Log.d("RASP","header");\r
+                               m = headerPattern.matcher(line);\r
+                               if ( m.matches() ) {\r
+                                       Log.d("RASP","header matches");\r
+                                       \r
+                                       /*motorName = m.group(1);*/\r
+                                       /*diameter = Integer.decode(m.group(2));*/\r
+                                       that.setLength(Float.parseFloat(m.group(3)) );\r
+                                       String delays = m.group(4);\r
+                                       if ( delays != null ) {\r
+                                               delays = delays.replace("-", ",");\r
+                                               that.setDelays(delays);\r
+                                       }\r
+                                       that.setPropWeightG(Double.parseDouble(m.group(5))*1000.0);\r
+                                       that.setTotWeightG(Double.parseDouble(m.group(6))*1000.0);\r
+                                       /*manufacturer = m.group(7);*/\r
+                                       \r
+                               }\r
+                               state = DATA;\r
+                               break;\r
+                               \r
+                       case DATA:\r
+                               Log.d("RASP","data");\r
+                               m = dataPattern.matcher(line);\r
+                               if ( m.matches() ) {\r
+                                       Log.d("RASP","data matches");\r
+                                       Double x = Double.parseDouble(m.group(1));\r
+                                       Double y = Double.parseDouble(m.group(2));\r
+                                       Log.d("RASP","data matches ("+x+","+y+")");\r
+                                       datapoints.add(x);\r
+                                       datapoints.add(y);\r
+                               }\r
+                               break;\r
+                       }\r
+                       that.setDatapoints(datapoints);\r
+               }\r
+               } catch (IOException ex ) {\r
+                       Log.d(TAG,"Unable to parse Rasp file: " + ex);\r
+               }\r
+               \r
+       }\r
+       \r
+\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/SearchRequest.java b/src/net/sf/openrocket/android/thrustcurve/SearchRequest.java
new file mode 100644 (file)
index 0000000..681c049
--- /dev/null
@@ -0,0 +1,117 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+public class SearchRequest {\r
+\r
+       private String manufacturer;\r
+       private String designation;\r
+       private String brand_name;\r
+       \r
+       private String common_name;\r
+       private String impulse_class;\r
+       private Integer diameter;\r
+       \r
+       /*\r
+       public enum Type {\r
+               "SU";\r
+               "reload";\r
+               "hybrid"\r
+       };\r
+       */\r
+       private String type;\r
+       \r
+       public void setManufacturer(String manufacturer) {\r
+               this.manufacturer = null;\r
+               if ( manufacturer != null ) {\r
+                       manufacturer = manufacturer.trim();\r
+                       if ( ! "".equals(manufacturer) ) {\r
+                               this.manufacturer = manufacturer;\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void setDesignation(String designation) {\r
+               this.designation = designation;\r
+       }\r
+\r
+       public void setBrand_name(String brand_name) {\r
+               this.brand_name = brand_name;\r
+       }\r
+\r
+       public void setCommon_name(String common_name) {\r
+               if ( common_name == null ) {\r
+                       this.common_name = null;\r
+                       return;\r
+               }\r
+               this.common_name = common_name.trim();\r
+               if ( "".equals(this.common_name)) {\r
+                       this.common_name = null;\r
+               }\r
+       }\r
+\r
+       public void setImpulse_class(String impulse_class) {\r
+               this.impulse_class = null;\r
+               if ( impulse_class != null ) {\r
+                       this.impulse_class = impulse_class.trim();\r
+                       if ( "".equals(impulse_class) ) {\r
+                               this.impulse_class = null;\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void setDiameter(Integer diameter) {\r
+               this.diameter = diameter;\r
+       }\r
+       \r
+       public void setDiameter(String diameter) {\r
+               this.diameter = null;\r
+               if ( diameter == null ) {\r
+                       return;\r
+               }\r
+               try {\r
+                       this.diameter = Integer.decode(diameter);\r
+               } catch ( NumberFormatException ex ) {\r
+                       this.diameter = null;\r
+               }\r
+       }\r
+\r
+       public void setType(String type) {\r
+               this.type = type;\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               StringBuilder w = new StringBuilder();\r
+               \r
+        w.append("<?xml version=\"1.0\" encoding=\"ascii\"?>\n");\r
+        w.append("<search-request\n");\r
+        w.append(" xmlns=\"http://www.thrustcurve.org/2011/SearchRequest\"\n");\r
+        w.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");\r
+        w.append(" xsi:schemaLocation=\"http://www.thrustcurve.org/2011/SearchRequest http://www.thrustcurve.org/2011/search-request.xsd\">\n");\r
+\r
+        if ( manufacturer != null ) {\r
+            w.append("  <manufacturer>").append(manufacturer).append("</manufacturer>\n");\r
+        }\r
+        if ( designation != null ) {\r
+            w.append("  <designation>").append(designation).append("</designation>\n");\r
+        }\r
+        if ( brand_name != null ) {\r
+            w.append("  <brand-name>").append(brand_name).append("</brand-name>\n");\r
+        }\r
+        if ( common_name != null ) {\r
+            w.append("  <common-name>").append(common_name).append("</common-name>\n");\r
+        }\r
+        if ( impulse_class != null ) {\r
+            w.append("  <impulse-class>").append(impulse_class).append("</impulse-class>\n");\r
+        }\r
+        if ( diameter != null ) {\r
+            w.append("  <diameter>").append(diameter).append("</diameter>\n");\r
+        }\r
+        if ( type != null ) {\r
+            w.append("  <type>").append(type).append("</type>\n");\r
+        }\r
+        w.append("<data-fields>*</data-fields>");\r
+        w.append("<max-results>50</max-results>");\r
+        w.append("</search-request>\n");\r
+               return w.toString();\r
+       }\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/SearchResponse.java b/src/net/sf/openrocket/android/thrustcurve/SearchResponse.java
new file mode 100644 (file)
index 0000000..48cbfef
--- /dev/null
@@ -0,0 +1,45 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+\r
+public class SearchResponse {\r
+\r
+       private List<TCMotor> results = new ArrayList<TCMotor>();\r
+       \r
+       private int matches;\r
+       \r
+       private String error;\r
+\r
+       public List<TCMotor> getResults() {\r
+               return results;\r
+       }\r
+\r
+       public void setResults(List<TCMotor> results) {\r
+               this.results = results;\r
+       }\r
+\r
+       public int getMatches() {\r
+               return matches;\r
+       }\r
+\r
+       public void setMatches(int matches) {\r
+               this.matches = matches;\r
+       }\r
+\r
+       public String getError() {\r
+               return error;\r
+       }\r
+\r
+       public void setError(String error) {\r
+               this.error = error;\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               return "SearchResult [results=" + results + ", matches=" + matches\r
+                               + ", error=" + error + "]";\r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/SearchResponseParser.java b/src/net/sf/openrocket/android/thrustcurve/SearchResponseParser.java
new file mode 100644 (file)
index 0000000..be53243
--- /dev/null
@@ -0,0 +1,281 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.InputStream;\r
+\r
+import org.xml.sax.Attributes;\r
+\r
+import android.sax.Element;\r
+import android.sax.EndElementListener;\r
+import android.sax.EndTextElementListener;\r
+import android.sax.RootElement;\r
+import android.sax.StartElementListener;\r
+import android.util.Xml;\r
+\r
+public class SearchResponseParser {\r
+\r
+       private static final String thrustcurveURI = "http://www.thrustcurve.org/2008/SearchResponse";\r
+       /*\r
+        * XML Tags in SearchResult xsd\r
+        */\r
+       private static final String root_tag = "search-response";\r
+       private static final String criteria = "criteria";\r
+       private static final String criterion = "criterion";\r
+       private static final String name = "name";\r
+       private static final String value = "value";\r
+       private static final String matches = "matches";\r
+       private static final String results = "results";\r
+       private static final String result = "result";\r
+       \r
+       private static final String motor_id = "motor-id";\r
+       private static final String manufacturer = "manufacturer";\r
+       private static final String manufacturer_abbr = "manufacturer-abbrev";\r
+       private static final String designation = "designation";\r
+       private static final String brand_name = "brand-name";\r
+       private static final String common_name = "common-name";\r
+       private static final String impulse_class = "impulse-class";\r
+       private static final String diameter = "diameter";\r
+       private static final String length = "length";\r
+       private static final String type = "type";\r
+       private static final String cert_org = "cert-org";\r
+       private static final String avg_thrust_n = "avg-thrust-n";\r
+       private static final String max_thrust_n = "max-thrust-n";\r
+       private static final String tot_impulse_ns = "tot-impulse-ns";\r
+       private static final String burn_time_s = "burn-time-s";\r
+       private static final String data_files = "data-files";\r
+       private static final String info_url = "info-url";\r
+       private static final String total_weight_g = "total-weight-g";\r
+       private static final String prop_weight_g = "prop-weight-g";\r
+       private static final String delays = "delays";\r
+       private static final String case_info = "case-info";\r
+       private static final String prop_info = "prop-info";\r
+       private static final String updated_on = "updated-on";\r
+       \r
+       public static SearchResponse parse( InputStream in ) {\r
+               \r
+               final SearchResponse ret = new SearchResponse();\r
+               final TCMotor currentMotor = new TCMotor();\r
+\r
+               RootElement rootEl = new RootElement(thrustcurveURI, root_tag);\r
+               Element criteriaEl = rootEl.getChild( thrustcurveURI, criteria);\r
+               \r
+               criteriaEl.getChild(thrustcurveURI,matches).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               ret.setMatches(Integer.parseInt(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               Element resultsEl = rootEl.getChild(thrustcurveURI,results);\r
+               Element resultEl = resultsEl.getChild(thrustcurveURI,result);\r
+\r
+               resultEl.setStartElementListener(\r
+                               new StartElementListener() {\r
+                                       @Override\r
+                                       public void start(Attributes arg0) {\r
+                                               currentMotor.init();\r
+                                       }\r
+                               }\r
+               );\r
+               \r
+               resultEl.setEndElementListener(\r
+                               new EndElementListener() {\r
+                                       @Override\r
+                                       public void end() {\r
+                                               ret.getResults().add((TCMotor)currentMotor.clone());\r
+                                       }\r
+                               }\r
+               );\r
+               \r
+               resultEl.getChild(thrustcurveURI,motor_id).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setMotor_id(Integer.parseInt(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,manufacturer).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               if ( arg0 != null ) {\r
+                                                       currentMotor.setManufacturer(arg0);\r
+                                               }\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,manufacturer_abbr).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               if ( arg0 != null ) {\r
+                                                       currentMotor.setManufacturer_abbr(arg0);\r
+                                               }\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,designation).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setDesignation(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,brand_name).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setBrand_name(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,common_name).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setCommon_name(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,impulse_class).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setImpulse_class(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,diameter).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setDiameter(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,length).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setLength(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,type).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setType(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,cert_org).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setCert_org(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,avg_thrust_n).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setAvg_thrust_n(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,max_thrust_n).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setMax_thrust_n(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,tot_impulse_ns).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setTot_impulse_ns(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,burn_time_s).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setBurn_time_s(Float.parseFloat(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,data_files).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setData_files(Integer.parseInt(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,info_url).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setInfo_url(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,total_weight_g).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setTot_mass_g(Double.parseDouble(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,prop_weight_g).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setProp_mass_g(Double.parseDouble(arg0));\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,delays).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setDelays(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,case_info).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setCase_info(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+               resultEl.getChild(thrustcurveURI,prop_info).setEndTextElementListener(\r
+                               new EndTextElementListener() {\r
+                                       @Override\r
+                                       public void end(String arg0) {\r
+                                               currentMotor.setProp_info(arg0);\r
+                                       }\r
+                               }\r
+               );\r
+\r
+\r
+        try {\r
+            Xml.parse(in, Xml.Encoding.UTF_8,  rootEl.getContentHandler());\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+\r
+               return ret;\r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/SupportedFileTypes.java b/src/net/sf/openrocket/android/thrustcurve/SupportedFileTypes.java
new file mode 100644 (file)
index 0000000..c123016
--- /dev/null
@@ -0,0 +1,11 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+public abstract class SupportedFileTypes {\r
+\r
+       public final static String ROCKSIM_FORMAT = "RockSim";\r
+       public final static String RASP_FORMAT = "RASP";\r
+       \r
+       public static boolean isSupportedFileType( String arg0 ) {\r
+               return (ROCKSIM_FORMAT.equals(arg0) || RASP_FORMAT.equals(arg0));\r
+       }\r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/TCMotor.java b/src/net/sf/openrocket/android/thrustcurve/TCMotor.java
new file mode 100644 (file)
index 0000000..477029e
--- /dev/null
@@ -0,0 +1,299 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.Date;\r
+import java.util.Vector;\r
+\r
+public class TCMotor implements Cloneable {\r
+\r
+       private Integer motor_id;\r
+       private String manufacturer;\r
+       private String manufacturer_abbr;\r
+       private String designation;\r
+       private String brand_name;\r
+       private String common_name;\r
+       private String impulse_class;\r
+       private Float diameter;\r
+       private Float length;\r
+       private String type;\r
+       private String cert_org;\r
+       private Float avg_thrust_n;\r
+       private Float max_thrust_n;\r
+       private Float tot_impulse_ns;\r
+       private Float burn_time_s;\r
+       private Integer data_files;\r
+       private String info_url;\r
+       private Double tot_mass_g;\r
+       private Double prop_mass_g;\r
+       private String delays;\r
+       private String case_info;\r
+       private String prop_info;\r
+       private Date updated_on;\r
+       private Vector<Double> burndata;\r
+       \r
+       public void init() {\r
+               motor_id = null;\r
+               manufacturer = null;\r
+               manufacturer_abbr = null;\r
+               designation = null;\r
+               brand_name = null;\r
+               common_name = null;\r
+               impulse_class = null;\r
+               diameter = null;\r
+               length = null;\r
+               type = null;\r
+               cert_org = null;\r
+               avg_thrust_n = null;\r
+               max_thrust_n = null;\r
+               tot_impulse_ns = null;\r
+               burn_time_s = null;\r
+               data_files = null;\r
+               info_url = null;\r
+               tot_mass_g = null;\r
+               prop_mass_g = null;\r
+               delays = null;\r
+               case_info = null;\r
+               prop_info = null;\r
+               updated_on = null;\r
+               burndata = null;\r
+       }\r
+       \r
+       @Override\r
+       public TCMotor clone() {\r
+               TCMotor clone = new TCMotor();\r
+               clone.motor_id = this.motor_id;\r
+               clone.manufacturer = this.manufacturer;\r
+               clone.manufacturer_abbr = this.manufacturer_abbr;\r
+               clone.designation = this.designation;\r
+               clone.brand_name = this.brand_name;\r
+               clone.common_name = this.common_name;\r
+               clone.impulse_class = this.impulse_class;\r
+               clone.diameter = this.diameter;\r
+               clone.length = this.length;\r
+               clone.type = this.type;\r
+               clone.cert_org = this.cert_org;\r
+               clone.avg_thrust_n = this.avg_thrust_n;\r
+               clone.max_thrust_n = this.max_thrust_n;\r
+               clone.tot_impulse_ns = this.tot_impulse_ns;\r
+               clone.burn_time_s = this.burn_time_s;\r
+               clone.data_files = this.data_files;\r
+               clone.info_url = this.info_url;\r
+               clone.tot_mass_g = this.tot_mass_g;\r
+               clone.prop_mass_g = this.prop_mass_g;\r
+               clone.delays = this.delays;\r
+               clone.case_info = this.case_info;\r
+               clone.prop_info = this.prop_info;\r
+               clone.updated_on = this.updated_on;\r
+               clone.burndata = this.burndata;\r
+               return clone;\r
+       }\r
+\r
+       public Integer getMotor_id() {\r
+               return motor_id;\r
+       }\r
+\r
+       public void setMotor_id(Integer motor_id) {\r
+               this.motor_id = motor_id;\r
+       }\r
+\r
+       public String getManufacturer() {\r
+               return manufacturer;\r
+       }\r
+\r
+       public void setManufacturer(String manufacturer) {\r
+               this.manufacturer = manufacturer;\r
+       }\r
+\r
+       public String getManufacturer_abbr() {\r
+               return manufacturer_abbr;\r
+       }\r
+\r
+       public void setManufacturer_abbr(String manufacturer_abbr) {\r
+               this.manufacturer_abbr = manufacturer_abbr;\r
+       }\r
+\r
+       public String getDesignation() {\r
+               return designation;\r
+       }\r
+\r
+       public void setDesignation(String designation) {\r
+               this.designation = designation;\r
+       }\r
+\r
+       public String getBrand_name() {\r
+               return brand_name;\r
+       }\r
+\r
+       public void setBrand_name(String brand_name) {\r
+               this.brand_name = brand_name;\r
+       }\r
+\r
+       public String getCommon_name() {\r
+               return common_name;\r
+       }\r
+\r
+       public void setCommon_name(String common_name) {\r
+               this.common_name = common_name;\r
+       }\r
+\r
+       public String getImpulse_class() {\r
+               return impulse_class;\r
+       }\r
+\r
+       public void setImpulse_class(String impulse_class) {\r
+               this.impulse_class = impulse_class;\r
+       }\r
+\r
+       public Float getDiameter() {\r
+               return diameter;\r
+       }\r
+\r
+       public void setDiameter(Float diameter) {\r
+               this.diameter = diameter;\r
+       }\r
+\r
+       public Float getLength() {\r
+               return length;\r
+       }\r
+\r
+       public void setLength(Float length) {\r
+               this.length = length;\r
+       }\r
+\r
+       public String getType() {\r
+               return type;\r
+       }\r
+\r
+       public void setType(String type) {\r
+               this.type = type;\r
+       }\r
+\r
+       public String getCert_org() {\r
+               return cert_org;\r
+       }\r
+\r
+       public void setCert_org(String cert_org) {\r
+               this.cert_org = cert_org;\r
+       }\r
+\r
+       public Float getAvg_thrust_n() {\r
+               return avg_thrust_n;\r
+       }\r
+\r
+       public void setAvg_thrust_n(Float avg_thrust_n) {\r
+               this.avg_thrust_n = avg_thrust_n;\r
+       }\r
+\r
+       public Float getMax_thrust_n() {\r
+               return max_thrust_n;\r
+       }\r
+\r
+       public void setMax_thrust_n(Float max_thrust_n) {\r
+               this.max_thrust_n = max_thrust_n;\r
+       }\r
+\r
+       public Float getTot_impulse_ns() {\r
+               return tot_impulse_ns;\r
+       }\r
+\r
+       public void setTot_impulse_ns(Float tot_impulse_ns) {\r
+               this.tot_impulse_ns = tot_impulse_ns;\r
+       }\r
+\r
+       public Float getBurn_time_s() {\r
+               return burn_time_s;\r
+       }\r
+\r
+       public void setBurn_time_s(Float burn_time_s) {\r
+               this.burn_time_s = burn_time_s;\r
+       }\r
+\r
+       public Integer getData_files() {\r
+               return data_files;\r
+       }\r
+\r
+       public void setData_files(Integer data_files) {\r
+               this.data_files = data_files;\r
+       }\r
+\r
+       public String getInfo_url() {\r
+               return info_url;\r
+       }\r
+\r
+       public void setInfo_url(String info_url) {\r
+               this.info_url = info_url;\r
+       }\r
+\r
+       public Double getTot_mass_g() {\r
+               return tot_mass_g;\r
+       }\r
+\r
+       public void setTot_mass_g(Double tot_mass_g) {\r
+               this.tot_mass_g = tot_mass_g;\r
+       }\r
+\r
+       public Double getProp_mass_g() {\r
+               return prop_mass_g;\r
+       }\r
+\r
+       public void setProp_mass_g(Double prop_mass_g) {\r
+               this.prop_mass_g = prop_mass_g;\r
+       }\r
+\r
+       public String getDelays() {\r
+               return delays;\r
+       }\r
+\r
+       public void setDelays(String delays) {\r
+               this.delays = delays;\r
+       }\r
+\r
+       public String getCase_info() {\r
+               return case_info;\r
+       }\r
+\r
+       public void setCase_info(String case_info) {\r
+               this.case_info = case_info;\r
+       }\r
+\r
+       public String getProp_info() {\r
+               return prop_info;\r
+       }\r
+\r
+       public void setProp_info(String prop_info) {\r
+               this.prop_info = prop_info;\r
+       }\r
+\r
+       public Date getUpdated_on() {\r
+               return updated_on;\r
+       }\r
+\r
+       public void setUpdated_on(Date updated_on) {\r
+               this.updated_on = updated_on;\r
+       }\r
+\r
+       public Vector<Double> getBurndata() {\r
+               return burndata;\r
+       }\r
+\r
+       public void setBurndata(Vector<Double> burndata) {\r
+               this.burndata = burndata;\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               return "TCMotor [motor_id=" + motor_id + ", manufacturer="\r
+                               + manufacturer + ", manufacturer_abbr=" + manufacturer_abbr\r
+                               + ", designation=" + designation + ", brand_name=" + brand_name\r
+                               + ", common_name=" + common_name + ", impulse_class="\r
+                               + impulse_class + ", diameter=" + diameter + ", length="\r
+                               + length + ", type=" + type + ", cert_org=" + cert_org\r
+                               + ", avg_thrust_n=" + avg_thrust_n + ", max_thrust_n="\r
+                               + max_thrust_n + ", tot_impulse_ns=" + tot_impulse_ns\r
+                               + ", burn_time_s=" + burn_time_s + ", data_files=" + data_files\r
+                               + ", info_url=" + info_url + ", tot_mass_g=" + tot_mass_g\r
+                               + ", prop_mass_g=" + prop_mass_g + ", delays=" + delays\r
+                               + ", case_info=" + case_info + ", prop_info=" + prop_info\r
+                               + ", updated_on=" + updated_on + "]";\r
+       }\r
+       \r
+}\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/TCQueryActivity.java b/src/net/sf/openrocket/android/thrustcurve/TCQueryActivity.java
new file mode 100644 (file)
index 0000000..8f5a783
--- /dev/null
@@ -0,0 +1,256 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.util.Vector;\r
+\r
+import net.sf.openrocket.R;\r
+import net.sf.openrocket.android.db.DbAdapter;\r
+import net.sf.openrocket.android.motor.Motor;\r
+import android.app.Activity;\r
+import android.app.AlertDialog;\r
+import android.app.ProgressDialog;\r
+import android.content.DialogInterface;\r
+import android.os.Bundle;\r
+import android.os.Handler;\r
+import android.util.Log;\r
+import android.view.View;\r
+import android.widget.Button;\r
+import android.widget.EditText;\r
+import android.widget.Spinner;\r
+\r
+public class TCQueryActivity extends Activity {\r
+\r
+       private static final String TAG = "ThrustCurveQueryActivity";\r
+\r
+       private DbAdapter mDbHelper;\r
+\r
+       private ProgressDialog progress;\r
+       private Thread downloadThread;\r
+       private Handler handler;\r
+\r
+       @Override\r
+       protected void onCreate(Bundle savedInstanceState) {\r
+               super.onCreate(savedInstanceState);\r
+               setContentView(R.layout.tcqueryform);\r
+\r
+               mDbHelper = new DbAdapter(this);\r
+               mDbHelper.open();\r
+\r
+               final Spinner manufacturerField = (Spinner) findViewById(R.id.TCMotorSearchFormManufacturerField);\r
+               final Spinner impulseField = (Spinner) findViewById(R.id.TCMotorSearchFormImpulseField);\r
+               final Spinner diameterField = (Spinner) findViewById(R.id.TCMotorSearchFormDiameterField);\r
+               final EditText commonNameField = (EditText) findViewById(R.id.TCMotorSearchFormCommonNameField);\r
+\r
+               Button submitButton = (Button) findViewById(R.id.TCMotorSearchFromSubmitButton);\r
+               submitButton.setOnClickListener(\r
+                               new View.OnClickListener() {\r
+                                       @Override\r
+                                       public void onClick( View v ) {\r
+                                               Log.d(TAG,"submit button clicked");\r
+\r
+                                               String commonName = commonNameField.getText().toString();\r
+\r
+                                               SearchRequest r = new SearchRequest();\r
+                                               if ( manufacturerField.getSelectedItemPosition() != 0) {\r
+                                                       String m = (String) manufacturerField.getSelectedItem();\r
+                                                       Log.d(TAG,"manufacturer = " + m);\r
+                                                       r.setManufacturer(m);\r
+                                               }\r
+                                               if ( impulseField.getSelectedItemPosition() != 0  ) {\r
+                                                       String impulse = (String) impulseField.getSelectedItem();\r
+                                                       Log.d(TAG,"impulse = " + impulse);\r
+                                                       r.setImpulse_class(impulse);\r
+                                               }\r
+                                               if ( diameterField.getSelectedItemPosition() != 0 ) {\r
+                                                       String diameter = (String)diameterField.getSelectedItem();\r
+                                                       Log.d(TAG,"diameter = " + diameter);\r
+                                                       r.setDiameter(diameter);\r
+                                               }\r
+                                               r.setCommon_name(commonName);\r
+\r
+                                               Downloader d = new Downloader(r);\r
+\r
+                                               handler = new Handler();\r
+                                               progress = ProgressDialog.show(TCQueryActivity.this, null, "");\r
+\r
+                                               downloadThread = new Thread( d );\r
+                                               downloadThread.start();\r
+                                       }\r
+                               }\r
+                               );\r
+       }\r
+\r
+       @Override\r
+       public Object onRetainNonConfigurationInstance() {\r
+               return downloadThread;\r
+       }\r
+\r
+       @Override\r
+       protected void onDestroy() {\r
+               mDbHelper.close();\r
+               if ( progress != null ) {\r
+                       if ( progress.isShowing() ) {\r
+                               progress.dismiss();\r
+                       }\r
+                       progress = null;\r
+               }\r
+               super.onDestroy();\r
+       }\r
+\r
+       private class UpdateMessage implements Runnable {\r
+               private String newMessage;\r
+               UpdateMessage( String message ) {\r
+                       this.newMessage = message;\r
+               }\r
+               @Override\r
+               public void run() {\r
+                       progress.setMessage(newMessage);\r
+               }\r
+       }\r
+\r
+       private class Dismiss implements Runnable {\r
+               @Override\r
+               public void run() {\r
+                       progress.dismiss();\r
+                       TCQueryActivity.this.finish();\r
+               }\r
+       }\r
+\r
+       private class Error implements Runnable {\r
+               private String newMessage;\r
+               Error( String message ) {\r
+                       this.newMessage = message;\r
+               }\r
+               @Override\r
+               public void run() {\r
+                       progress.dismiss();\r
+                       final AlertDialog dialog = new AlertDialog.Builder(TCQueryActivity.this).create();\r
+                       dialog.setMessage(newMessage);\r
+                       dialog.setButton(DialogInterface.BUTTON_NEUTRAL,"Dismiss", new DialogInterface.OnClickListener() {\r
+\r
+                               @Override\r
+                               public void onClick(DialogInterface arg0, int arg1) {\r
+                                       dialog.dismiss();\r
+                               }\r
+\r
+                       });\r
+                       dialog.show();\r
+               }\r
+       }\r
+       private class Downloader implements Runnable {\r
+\r
+               SearchRequest request;\r
+\r
+               Downloader( SearchRequest request ) {\r
+                       this.request = request;\r
+               }\r
+\r
+               @Override\r
+               public void run() {\r
+                       try {\r
+                               handler.post( new UpdateMessage("Quering Thrustcurve"));\r
+                               SearchResponse res = new ThrustCurveAPI().doSearch(request);\r
+\r
+                               int total = res.getResults().size();\r
+                               int count = 1;\r
+                               for( TCMotor mi : res.getResults() ) {\r
+                                       handler.post(new UpdateMessage("Downloading details " + count + " of " + total));\r
+                                       count++;\r
+                                       if ( mi.getData_files() == null || mi.getData_files().intValue() == 0 ) {\r
+                                               continue;\r
+                                       }\r
+\r
+                                       MotorBurnFile b = new ThrustCurveAPI().downloadData(mi.getMotor_id());\r
+\r
+                                       if ( b != null ) {\r
+                                               if ( b.getLength() != null ) {\r
+                                                       mi.setLength( b.getLength() );\r
+                                               }\r
+                                               if ( b.getPropWeightG() != null ) {\r
+                                                       mi.setProp_mass_g(b.getPropWeightG());\r
+                                               }\r
+                                               if ( b.getTotWeightG() != null ) {\r
+                                                       mi.setTot_mass_g(b.getTotWeightG());\r
+                                               }\r
+                                               if ( b.getDelays() != null ) {\r
+                                                       mi.setDelays(b.getDelays());\r
+                                               }\r
+                                               mi.setBurndata(b.getDatapoints());\r
+                                       }\r
+                                       Log.d(TAG, mi.toString());\r
+\r
+                                       // convert to Motors.  One per delay.\r
+                                       Motor m = new Motor();\r
+                                       // Base name of motor.\r
+                                       String name = mi.getCommon_name() + "-";\r
+\r
+                                       m.setManufacturer(mi.getManufacturer_abbr());\r
+                                       // Convert impulse class.  ThrustCurve puts mmx, 1/4a and 1/2a as A.\r
+                                       m.setImpulseClass(mi.getImpulse_class());\r
+                                       if ( "a".equalsIgnoreCase(mi.getImpulse_class())) {\r
+                                               if( mi.getCommon_name().startsWith("1/2A") ) {\r
+                                                       m.setImpulseClass("1/2A");\r
+                                               } else if (mi.getCommon_name().startsWith("1/4A") ) {\r
+                                                       m.setImpulseClass("1/4A");\r
+                                               } else if (mi.getCommon_name().startsWith("Micro") ) {\r
+                                                       m.setImpulseClass("1/8A");\r
+                                               }\r
+                                       }\r
+                                       m.setAvgThrust(mi.getAvg_thrust_n());\r
+                                       m.setBurndata(mi.getBurndata());\r
+                                       m.setBurnTime(mi.getBurn_time_s());\r
+                                       m.setDiameter(mi.getDiameter() == null ? null : mi.getDiameter().longValue());\r
+                                       m.setLength(mi.getLength());\r
+                                       m.setMaxThrust(mi.getMax_thrust_n());\r
+                                       m.setPropMass(mi.getProp_mass_g());\r
+                                       m.setTotalImpulse(mi.getTot_impulse_ns());\r
+                                       m.setTotMass(mi.getTot_mass_g());\r
+                                       // Convert Case Info.\r
+                                       if ( mi.getCase_info() == null\r
+                                                       || "single use".equalsIgnoreCase(mi.getCase_info())\r
+                                                       || "single-use".equalsIgnoreCase(mi.getCase_info())) {\r
+                                               m.setCaseInfo(mi.getType()+ " " + mi.getDiameter() + "x" + mi.getLength());\r
+                                       } else {\r
+                                               m.setCaseInfo(mi.getCase_info());\r
+                                       }\r
+\r
+                                       Vector<String> delays = new Vector<String>();\r
+                                       {\r
+                                               String delaysString = mi.getDelays();\r
+                                               if ( delaysString != null ) {\r
+                                                       delaysString = delaysString.trim();\r
+                                               }\r
+\r
+                                               if ( delaysString == null || "".equals(delaysString)) {\r
+                                                       delays.add("");\r
+                                               } else {\r
+                                                       String[] delayString = delaysString.split(",");\r
+                                                       for( String d : delayString )  {\r
+                                                               delays.add( d.trim() );\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       for( String d: delays ) {\r
+                                               if ( "100".equals(d) ) {\r
+                                                       m.setName(name + "P");\r
+                                               } else {\r
+                                                       m.setName(name + d);\r
+                                               }\r
+                                               mDbHelper.getMotorDao().insertOrUpdateMotor(m);\r
+                                       }\r
+                               }\r
+                               if ( total < res.getMatches() ) {\r
+                                       handler.post( new Error( total + " motors downloaded, " + res.getMatches() + " matched.  Try restricting the query more.") );\r
+                               } else {\r
+                                       handler.post( new Dismiss());\r
+                               }\r
+                       }\r
+                       catch( Exception ex){\r
+                               Log.d(TAG,ex.toString());\r
+                               handler.post( new Error(ex.toString()) );\r
+                       }\r
+\r
+               }\r
+       }\r
+}\r
+\r
diff --git a/src/net/sf/openrocket/android/thrustcurve/ThrustCurveAPI.java b/src/net/sf/openrocket/android/thrustcurve/ThrustCurveAPI.java
new file mode 100644 (file)
index 0000000..9241228
--- /dev/null
@@ -0,0 +1,80 @@
+package net.sf.openrocket.android.thrustcurve;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.net.URLConnection;\r
+\r
+import android.util.Log;\r
+\r
+\r
+public class ThrustCurveAPI {\r
+\r
+       private final static String TAG = "ThrustCurveAPI";\r
+       \r
+       private String url_base = "http://www.thrustcurve.org/servlets/";\r
+       \r
+       public SearchResponse doSearch( SearchRequest request ) throws MalformedURLException, IOException {\r
+               \r
+               String requestString = request.toString();\r
+               \r
+               Log.d(TAG, "doSearch: " + requestString);\r
+               URL url = new URL(url_base + "search");\r
+\r
+        OutputStream  stream;\r
+\r
+        URLConnection conn = url.openConnection();\r
+        conn.setConnectTimeout(2000);\r
+        conn.setDoInput(true);\r
+        conn.setDoOutput(true);\r
+        conn.setUseCaches(false);\r
+\r
+        stream = conn.getOutputStream();\r
+\r
+        stream.write(requestString.getBytes());\r
+        \r
+        InputStream is = conn.getInputStream();\r
+\r
+        SearchResponse result = SearchResponseParser.parse(is);\r
+        Log.d(TAG,result.toString());\r
+        \r
+        return result;\r
+       }\r
+\r
+       public MotorBurnFile downloadData( Integer motor_id ) throws MalformedURLException, IOException {\r
+\r
+               if ( motor_id == null ) {\r
+                       return null;\r
+               }\r
+               DownloadRequest dr = new DownloadRequest();\r
+               dr.add(motor_id);\r
+\r
+               String requestString = dr.toString();\r
+\r
+               Log.d(TAG, "downloadData: " + requestString);\r
+               URL url = new URL(url_base + "download");\r
+\r
+               OutputStream  stream;\r
+\r
+               URLConnection conn = url.openConnection();\r
+               conn.setDoInput(true);\r
+               conn.setDoOutput(true);\r
+               conn.setUseCaches(false);\r
+\r
+               stream = conn.getOutputStream();\r
+\r
+               stream.write(requestString.getBytes());\r
+\r
+               InputStream is = conn.getInputStream();\r
+\r
+               DownloadResponse downloadResponse = DownloadResponseParser.parse(is);\r
+               Log.d(TAG,downloadResponse.toString());\r
+\r
+               MotorBurnFile mbf = downloadResponse.getData(motor_id);\r
+\r
+               return mbf;\r
+\r
+    }\r
+}\r
diff --git a/src/net/sf/openrocket/database/MotorDatabase.java b/src/net/sf/openrocket/database/MotorDatabase.java
new file mode 100644 (file)
index 0000000..fdde5fa
--- /dev/null
@@ -0,0 +1,24 @@
+package net.sf.openrocket.database;\r
+\r
+import java.util.List;\r
+\r
+import net.sf.openrocket.motor.Motor;\r
+\r
+public interface MotorDatabase {\r
+\r
+       /**\r
+        * Return all motors in the database matching a search criteria.  Any search criteria that\r
+        * is null or NaN is ignored.\r
+        * \r
+        * @param type                  the motor type, or null.\r
+        * @param manufacturer  the manufacturer, or null.\r
+        * @param designation   the designation, or null.\r
+        * @param diameter              the diameter, or NaN.\r
+        * @param length                the length, or NaN.\r
+        * @return                              a list of all the matching motors.\r
+        */\r
+       public List<? extends Motor> findMotors(Motor.Type type,\r
+                       String manufacturer, String designation, double diameter,\r
+                       double length);\r
+\r
+}
\ No newline at end of file
index 99c422e314c1a515217e5e758539e98ce799cac4..321e42b269cf102fc95f1b042bbfd694a3ab20d8 100644 (file)
@@ -19,7 +19,7 @@ import net.sf.openrocket.motor.ThrustCurveMotor;
 import net.sf.openrocket.util.ArrayList;
 import net.sf.openrocket.util.MathUtil;
 
-public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
+class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
        
        //  Comparators:
        private static final Collator COLLATOR = Collator.getInstance(Locale.US);
index d1511595dc1d10edf5b30fe71e707dd4d75c75e6..1aeb6cd4c70e456c0e9abaa433f506fc39514f0a 100644 (file)
@@ -15,7 +15,7 @@ import net.sf.openrocket.startup.Application;
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
-public abstract class ThrustCurveMotorSetDatabase {
+public abstract class ThrustCurveMotorSetDatabase implements MotorDatabase {
        
        private static final LogHelper logger = Application.getLogger();
        
@@ -38,11 +38,8 @@ public abstract class ThrustCurveMotorSetDatabase {
        }
        
        
-       /**
-        * Return a list of the ThrustCurveMotorSet objects.  The list is in sorted order and
-        * is unmodifiable.
-        * 
-        * @return              the list of all ThrustCurveMotorSets.
+       /* (non-Javadoc)
+        * @see net.sf.openrocket.database.ThrustCurveMotorSetDatabaseI#getMotorSets()
         */
        public List<ThrustCurveMotorSet> getMotorSets() {
                blockUntilLoaded();
@@ -51,17 +48,10 @@ public abstract class ThrustCurveMotorSetDatabase {
        
        
 
-       /**
-        * Return all motors in the database matching a search criteria.  Any search criteria that
-        * is null or NaN is ignored.
-        * 
-        * @param type                  the motor type, or null.
-        * @param manufacturer  the manufacturer, or null.
-        * @param designation   the designation, or null.
-        * @param diameter              the diameter, or NaN.
-        * @param length                the length, or NaN.
-        * @return                              a list of all the matching motors.
+       /* (non-Javadoc)
+        * @see net.sf.openrocket.database.ThrustCurveMotorSetDatabaseI#findMotors(net.sf.openrocket.motor.Motor.Type, java.lang.String, java.lang.String, double, double)
         */
+       @Override
        public List<ThrustCurveMotor> findMotors(Motor.Type type, String manufacturer, String designation,
                        double diameter, double length) {
                blockUntilLoaded();
index 26400c0bc11cb0e198d6db2cc2a5bc18b4e288b2..c4fc7d7376a0ccadd767483e84f31165f26dbd89 100644 (file)
@@ -999,7 +999,7 @@ class MotorHandler extends ElementHandler {
                        return null;
                }
                
-               List<ThrustCurveMotor> motors = Application.getMotorSetDatabase().findMotors(type, manufacturer,
+               List<? extends Motor> motors = Application.getMotorSetDatabase().findMotors(type, manufacturer,
                                designation, diameter, length);
                
                // No motors
@@ -1014,7 +1014,7 @@ class MotorHandler extends ElementHandler {
                
                // One motor
                if (motors.size() == 1) {
-                       ThrustCurveMotor m = motors.get(0);
+                       Motor m = motors.get(0);
                        if (digest != null && !MotorDigest.digestMotor(m).equals(digest)) {
                                String str = "Motor with designation '" + designation + "'";
                                if (manufacturer != null)
@@ -1029,7 +1029,7 @@ class MotorHandler extends ElementHandler {
                if (digest != null) {
                        
                        // Check for motor with correct digest
-                       for (ThrustCurveMotor m : motors) {
+                       for (Motor m : motors) {
                                if (MotorDigest.digestMotor(m).equals(digest)) {
                                        return m;
                                }
@@ -1044,7 +1044,7 @@ class MotorHandler extends ElementHandler {
                        
                        // No digest, check for preferred digest (OpenRocket <= 1.1.0)
                        // TODO: MEDIUM: This should only be done for document versions 1.1 and below
-                       for (ThrustCurveMotor m : motors) {
+                       for (Motor m : motors) {
                                if (PreferredMotorDigests.DIGESTS.contains(MotorDigest.digestMotor(m))) {
                                        return m;
                                }
index ae83e7c4d7418833c54931205b60b957b99108d5..fda0eebb971c8753b534ecbb8e6cd32cd23a61ba 100644 (file)
@@ -126,5 +126,8 @@ public interface Motor {
        public double getMaxThrustEstimate();
        public double getTotalImpulseEstimate();
        
+       public double[] getTimePoints();
+       public double[] getThrustPoints();
+       public Coordinate[] getCGPoints();
        
 }
index cec3cbdc6082a37c7a43354815d7afa5effa9e12..e50cbf9f2197aac9d56112c901035380c5a7dc6f 100644 (file)
@@ -138,7 +138,7 @@ public class MotorDigest {
         * @param m             the motor to digest
         * @return              the digest
         */
-       public static String digestMotor(ThrustCurveMotor m) {
+       public static String digestMotor(Motor m) {
                
                // Create the motor digest from data available in RASP files
                MotorDigest motorDigest = new MotorDigest();
index a51283eaefd341c447d32c861c807b187ce0349c..1f8acd2aa8a0316e877491138d0d3f0d7b9d3f24 100644 (file)
@@ -1,5 +1,6 @@
 package net.sf.openrocket.motor;
 
+import java.io.Serializable;
 import java.text.Collator;
 import java.util.Arrays;
 import java.util.Locale;
index 9ced3a34f0981ee231f7f98fc51cc6eb41b11712..7718b67ec0f94504c844b6c50cb2ee31d43277c1 100644 (file)
@@ -1,6 +1,6 @@
 package net.sf.openrocket.startup;
 
-import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
+import net.sf.openrocket.database.MotorDatabase;
 import net.sf.openrocket.l10n.ClassBasedTranslator;
 import net.sf.openrocket.l10n.DebugTranslator;
 import net.sf.openrocket.l10n.ExceptionSuppressingTranslator;
@@ -22,7 +22,7 @@ public final class Application {
        
        private static Translator baseTranslator = new DebugTranslator(null);
        
-       private static ThrustCurveMotorSetDatabase motorSetDatabase;
+       private static MotorDatabase motorSetDatabase;
 
        private static Preferences preferences;
        
@@ -149,14 +149,14 @@ public final class Application {
        /**
         * Return the database of all thrust curves loaded into the system.
         */
-       public static ThrustCurveMotorSetDatabase getMotorSetDatabase() {
+       public static MotorDatabase getMotorSetDatabase() {
                return motorSetDatabase;
        }
        
        /**
         * Set the database of thrust curves loaded into the system.
         */
-       public static void setMotorSetDatabase(ThrustCurveMotorSetDatabase motorSetDatabase) {
+       public static void setMotorSetDatabase(MotorDatabase motorSetDatabase) {
                Application.motorSetDatabase = motorSetDatabase;
        }