Move java source, and resources to new paths for gradle
authorMike Beattie <mike@ethernal.org>
Wed, 18 Sep 2019 22:04:58 +0000 (10:04 +1200)
committerMike Beattie <mike@ethernal.org>
Wed, 18 Sep 2019 22:04:58 +0000 (10:04 +1200)
Signed-off-by: Mike Beattie <mike@ethernal.org>
134 files changed:
altosdroid/AndroidManifest.xml.in [deleted file]
altosdroid/app/src/main/AndroidManifest.xml.in [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosBluetooth.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDebug.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroid.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidLink.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidTab.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosUsb.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosViewPager.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosVoice.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/BuildInfo.java.in [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceAddress.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceListActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Dumper.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/GoNoGoLights.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IgniterActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/MapTypeActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/PreloadMapActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SetupActivity.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabFlight.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabMap.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabPad.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabRecover.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabsAdapter.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryLogger.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryReader.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java [new file with mode: 0644]
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-hdpi/app_icon.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-hdpi/ic_maps_indicator_current_position.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable-mdpi/ic_maps_indicator_current_position.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable/app_icon.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable/pad.png [new file with mode: 0644]
altosdroid/app/src/main/res/drawable/rocket.png [new file with mode: 0644]
altosdroid/app/src/main/res/layout/altosdroid.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/custom_title.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/device_list.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/device_name.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/frequency.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/idle_mode.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/igniter_status.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/igniters.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/manage_frequencies.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/map_preload.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/map_type.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/setup.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/tab_flight.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/tab_layout.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/tab_map.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/tab_pad.xml [new file with mode: 0644]
altosdroid/app/src/main/res/layout/tab_recover.xml [new file with mode: 0644]
altosdroid/app/src/main/res/menu/option_menu.xml [new file with mode: 0644]
altosdroid/app/src/main/res/values/Colors.xml [new file with mode: 0644]
altosdroid/app/src/main/res/values/CustomTheme.xml [new file with mode: 0644]
altosdroid/app/src/main/res/values/strings.xml [new file with mode: 0644]
altosdroid/app/src/main/res/xml/device_filter.xml [new file with mode: 0644]
altosdroid/res/drawable-hdpi/am_status_c.png [deleted file]
altosdroid/res/drawable-hdpi/am_status_g.png [deleted file]
altosdroid/res/drawable-hdpi/app_icon.png [deleted file]
altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png [deleted file]
altosdroid/res/drawable-mdpi/am_status_c.png [deleted file]
altosdroid/res/drawable-mdpi/am_status_g.png [deleted file]
altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png [deleted file]
altosdroid/res/drawable/app_icon.png [deleted file]
altosdroid/res/drawable/pad.png [deleted file]
altosdroid/res/drawable/rocket.png [deleted file]
altosdroid/res/layout/altosdroid.xml [deleted file]
altosdroid/res/layout/custom_title.xml [deleted file]
altosdroid/res/layout/device_list.xml [deleted file]
altosdroid/res/layout/device_name.xml [deleted file]
altosdroid/res/layout/frequency.xml [deleted file]
altosdroid/res/layout/idle_mode.xml [deleted file]
altosdroid/res/layout/igniter_status.xml [deleted file]
altosdroid/res/layout/igniters.xml [deleted file]
altosdroid/res/layout/manage_frequencies.xml [deleted file]
altosdroid/res/layout/map_preload.xml [deleted file]
altosdroid/res/layout/map_type.xml [deleted file]
altosdroid/res/layout/setup.xml [deleted file]
altosdroid/res/layout/tab_flight.xml [deleted file]
altosdroid/res/layout/tab_layout.xml [deleted file]
altosdroid/res/layout/tab_map.xml [deleted file]
altosdroid/res/layout/tab_pad.xml [deleted file]
altosdroid/res/layout/tab_recover.xml [deleted file]
altosdroid/res/menu/option_menu.xml [deleted file]
altosdroid/res/values/Colors.xml [deleted file]
altosdroid/res/values/CustomTheme.xml [deleted file]
altosdroid/res/values/strings.xml [deleted file]
altosdroid/res/xml/device_filter.xml [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/BuildInfo.java.in [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java [deleted file]

diff --git a/altosdroid/AndroidManifest.xml.in b/altosdroid/AndroidManifest.xml.in
deleted file mode 100644 (file)
index 690f199..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2012 Mike Beattie <mike@ethernal.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="org.altusmetrum.AltosDroid"
-          android:versionCode="@ANDROID_VERSION@"
-          android:versionName="@VERSION@">
-    <uses-sdk android:targetSdkVersion="21" android:minSdkVersion="21"/>
-    <!-- Google Maps -->
-    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
-
-    <!-- Permissions needed to access bluetooth -->
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-    <uses-permission android:name="android.permission.BLUETOOTH" />
-    <!-- Permissions needed to save Telemetry logs to SD card -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <!-- Permissions needed for GoogleMaps -->
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
-    <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
-                android:protectionLevel="signature"/>
-    <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
-
-    <!-- Permissions needed to access USB OTG -->
-    <uses-feature android:name="android.hardware.usb.host" android:required="false" />
-
-    <application android:label="@string/app_name"
-                 android:icon="@drawable/app_icon"
-                 android:allowBackup="true"
-                android:theme="@style/CustomTheme">
-        <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
-                  android:label="@string/app_name"
-                  android:configChanges="orientation|keyboardHidden"
-                 android:launchMode="singleTop">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-
-       <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
-                  android:configChanges="orientation|keyboardHidden"
-                 android:launchMode="singleTop">
-         <intent-filter>
-           <action
-               android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
-         </intent-filter>
-         <meta-data
-             android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
-             android:resource="@xml/device_filter" />
-       </activity>
-
-        <activity android:name=".DeviceListActivity"
-                  android:label="@string/select_device"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboardHidden" />
-
-        <activity android:name=".PreloadMapActivity"
-                  android:label="@string/preload_maps"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboardHidden" />
-
-        <activity android:name=".MapTypeActivity"
-                  android:label="@string/map_type"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboardHidden" />
-
-        <activity android:name=".IdleModeActivity"
-                  android:label="@string/idle_mode"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboardHidden" />
-
-        <activity android:name=".IgniterActivity"
-                  android:label="@string/igniters"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboardHidden" />
-
-       <activity android:name=".SetupActivity"
-                 android:label="@string/setup"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation" />
-                 
-       <activity android:name=".ManageFrequenciesActivity"
-                 android:label="@string/manage_frequencies"
-                  android:theme="@android:style/Theme.Dialog"
-                  android:configChanges="orientation|keyboard" />
-                 
-        <service android:name=".TelemetryService" />
-
-        <meta-data android:name="com.google.android.maps.v2.API_KEY"
-                   android:value="@GOOGLEKEY@"/>
-        <meta-data android:name="com.google.android.gms.version"
-                   android:value="@integer/google_play_services_version" />
-    </application>
-</manifest>
diff --git a/altosdroid/app/src/main/AndroidManifest.xml.in b/altosdroid/app/src/main/AndroidManifest.xml.in
new file mode 100644 (file)
index 0000000..690f199
--- /dev/null
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2012 Mike Beattie <mike@ethernal.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="org.altusmetrum.AltosDroid"
+          android:versionCode="@ANDROID_VERSION@"
+          android:versionName="@VERSION@">
+    <uses-sdk android:targetSdkVersion="21" android:minSdkVersion="21"/>
+    <!-- Google Maps -->
+    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
+
+    <!-- Permissions needed to access bluetooth -->
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <!-- Permissions needed to save Telemetry logs to SD card -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <!-- Permissions needed for GoogleMaps -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+    <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
+                android:protectionLevel="signature"/>
+    <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
+
+    <!-- Permissions needed to access USB OTG -->
+    <uses-feature android:name="android.hardware.usb.host" android:required="false" />
+
+    <application android:label="@string/app_name"
+                 android:icon="@drawable/app_icon"
+                 android:allowBackup="true"
+                android:theme="@style/CustomTheme">
+        <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+                  android:label="@string/app_name"
+                  android:configChanges="orientation|keyboardHidden"
+                 android:launchMode="singleTop">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+       <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+                  android:configChanges="orientation|keyboardHidden"
+                 android:launchMode="singleTop">
+         <intent-filter>
+           <action
+               android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
+         </intent-filter>
+         <meta-data
+             android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+             android:resource="@xml/device_filter" />
+       </activity>
+
+        <activity android:name=".DeviceListActivity"
+                  android:label="@string/select_device"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".PreloadMapActivity"
+                  android:label="@string/preload_maps"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".MapTypeActivity"
+                  android:label="@string/map_type"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".IdleModeActivity"
+                  android:label="@string/idle_mode"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".IgniterActivity"
+                  android:label="@string/igniters"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+       <activity android:name=".SetupActivity"
+                 android:label="@string/setup"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation" />
+                 
+       <activity android:name=".ManageFrequenciesActivity"
+                 android:label="@string/manage_frequencies"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboard" />
+                 
+        <service android:name=".TelemetryService" />
+
+        <meta-data android:name="com.google.android.maps.v2.API_KEY"
+                   android:value="@GOOGLEKEY@"/>
+        <meta-data android:name="com.google.android.gms.version"
+                   android:value="@integer/google_play_services_version" />
+    </application>
+</manifest>
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosBluetooth.java
new file mode 100644 (file)
index 0000000..0d09abd
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+//import android.os.Bundle;
+import android.os.Handler;
+//import android.os.Message;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosBluetooth extends AltosDroidLink {
+
+       private ConnectThread    connect_thread = null;
+
+       private BluetoothDevice  device;
+       private BluetoothSocket  socket;
+       private InputStream      input;
+       private OutputStream     output;
+       private boolean          pause;
+
+       // Constructor
+       public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
+               super(handler);
+               this.device = device;
+               this.handler = handler;
+               this.pause = pause;
+
+               connect_thread = new ConnectThread();
+               connect_thread.start();
+       }
+
+       void connected() {
+               if (closed()) {
+                       AltosDebug.debug("connected after closed");
+                       return;
+               }
+
+               AltosDebug.check_ui("connected\n");
+               try {
+                       synchronized(this) {
+                               if (socket != null) {
+                                       input = socket.getInputStream();
+                                       output = socket.getOutputStream();
+                                       super.connected();
+                               }
+                       }
+               } catch (InterruptedException ie) {
+                       connect_failed();
+               } catch (IOException io) {
+                       connect_failed();
+               }
+       }
+
+       private void connect_failed() {
+               if (closed()) {
+                       AltosDebug.debug("connect_failed after closed");
+                       return;
+               }
+
+               close_device();
+               input = null;
+               output = null;
+               handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
+               AltosDebug.error("ConnectThread: Failed to establish connection");
+       }
+
+       void close_device() {
+               BluetoothSocket tmp_socket;
+
+               synchronized(this) {
+                       tmp_socket = socket;
+                       socket = null;
+               }
+
+               if (tmp_socket != null) {
+                       try {
+                               tmp_socket.close();
+                       } catch (IOException e) {
+                               AltosDebug.error("close_socket failed");
+                       }
+               }
+       }
+
+       public void close() {
+               super.close();
+               input = null;
+               output = null;
+       }
+
+       private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
+
+       private void create_socket(BluetoothDevice  device) {
+
+               BluetoothSocket tmp_socket = null;
+
+               AltosDebug.check_ui("create_socket\n");
+               try {
+                       tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+               if (socket != null) {
+                       AltosDebug.debug("Socket already allocated %s", socket.toString());
+                       close_device();
+               }
+               synchronized (this) {
+                       socket = tmp_socket;
+               }
+       }
+
+       private class ConnectThread extends Thread {
+
+               public void run() {
+                       AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
+                       setName("ConnectThread");
+
+                       if (pause) {
+                               try {
+                                       Thread.sleep(4000);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+
+                       create_socket(device);
+                       // Always cancel discovery because it will slow down a connection
+                       try {
+                               BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
+                       } catch (Exception e) {
+                               AltosDebug.debug("cancelDiscovery exception %s", e.toString());
+                       }
+
+                       BluetoothSocket local_socket = null;
+
+                       synchronized (AltosBluetooth.this) {
+                               if (!closed())
+                                       local_socket = socket;
+                       }
+
+                       if (local_socket != null) {
+                               try {
+                                       // Make a connection to the BluetoothSocket
+                                       // This is a blocking call and will only return on a
+                                       // successful connection or an exception
+                                       local_socket.connect();
+                               } catch (Exception e) {
+                                       AltosDebug.debug("Connect exception %s", e.toString());
+                                       try {
+                                               local_socket.close();
+                                       } catch (Exception ce) {
+                                               AltosDebug.debug("Close exception %s", ce.toString());
+                                       }
+                                       local_socket = null;
+                               }
+                       }
+
+                       if (local_socket != null) {
+                               connected();
+                       } else {
+                               connect_failed();
+                       }
+
+                       AltosDebug.debug("ConnectThread: completed");
+               }
+       }
+
+       private synchronized void wait_connected() throws InterruptedException, IOException {
+               AltosDebug.check_ui("wait_connected\n");
+               if (input == null && socket != null) {
+                       AltosDebug.debug("wait_connected...");
+                       wait();
+                       AltosDebug.debug("wait_connected done");
+               }
+               if (socket == null)
+                       throw new IOException();
+       }
+
+       int write(byte[] buffer, int len) {
+               if (output == null)
+                       return -1;
+               try {
+                       output.write(buffer, 0, len);
+               } catch (IOException ie) {
+                       return -1;
+               }
+               return len;
+       }
+
+       int read(byte[] buffer, int len) {
+               if (input == null)
+                       return -1;
+               try {
+                       return input.read(buffer, 0, len);
+               } catch (IOException ie) {
+                       return -1;
+               }
+       }
+
+       // Stubs of required methods when extending AltosLink
+       public boolean can_cancel_reply()   { return false; }
+       public boolean show_reply_timeout() { return true; }
+       public void hide_reply_timeout()    { }
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDebug.java
new file mode 100644 (file)
index 0000000..2ae0652
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+package org.altusmetrum.AltosDroid;
+
+import java.util.Arrays;
+import java.io.*;
+import java.lang.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.Log;
+import android.os.*;
+import android.content.pm.*;
+
+public class AltosDebug {
+       // Debugging
+       static final String TAG = "AltosDroid";
+
+       static boolean  D = true;
+
+       static void init(Context context) {
+               ApplicationInfo app_info = context.getApplicationInfo();
+
+               if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                       Log.d(TAG, "Enable debugging\n");
+                       D = true;
+               } else {
+                       Log.d(TAG, "Disable debugging\n");
+                       D = false;
+               }
+       }
+
+
+       static void info(String format, Object ... arguments) {
+               Log.i(TAG, String.format(format, arguments));
+       }
+
+       static void debug(String format, Object ... arguments) {
+               if (D)
+                       Log.d(TAG, String.format(format, arguments));
+       }
+
+       static void error(String format, Object ... arguments) {
+               Log.e(TAG, String.format(format, arguments));
+       }
+
+       static void check_ui(String format, Object ... arguments) {
+               if (Looper.myLooper() == Looper.getMainLooper()) {
+                       Log.e(TAG, String.format("ON UI THREAD " + format, arguments));
+                       for (StackTraceElement el : Thread.currentThread().getStackTrace())
+                               Log.e(TAG, "\t" + el.toString() + "\n");
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroid.java
new file mode 100644 (file)
index 0000000..1bcb67e
--- /dev/null
@@ -0,0 +1,1224 @@
+/*
+ * Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.text.*;
+import java.util.*;
+import java.io.*;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.content.DialogInterface;
+import android.os.IBinder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.content.res.Resources;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.util.DisplayMetrics;
+import android.view.*;
+import android.widget.*;
+import android.app.AlertDialog;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.hardware.usb.*;
+import android.graphics.*;
+import android.graphics.drawable.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+class SavedState {
+       long    received_time;
+       int     state;
+       boolean locked;
+       String  callsign;
+       int     serial;
+       int     flight;
+       int     rssi;
+
+       SavedState(AltosState state) {
+               received_time = state.received_time;
+               this.state = state.state();
+               if (state.gps != null)
+                       locked = state.gps.locked;
+               else
+                       locked = false;
+               callsign = state.cal_data().callsign;
+               serial = state.cal_data().serial;
+               flight = state.cal_data().flight;
+               rssi = state.rssi;
+       }
+}
+
+class Tracker implements CharSequence, Comparable {
+       int     serial;
+       String  call;
+       double  frequency;
+
+       String  display;
+
+       public Tracker(int serial, String call, double frequency) {
+               if (call == null)
+                       call = "none";
+
+               this.serial = serial;
+               this.call = call;
+               this.frequency = frequency;
+               if (frequency == 0.0)
+                       display = "Auto";
+               else if (frequency == AltosLib.MISSING) {
+                       display = String.format("%-8.8s  %6d", call, serial);
+               } else {
+                       display = String.format("%-8.8s %7.3f %6d", call, frequency, serial);
+               }
+       }
+
+       public Tracker(AltosState s) {
+               this(s == null ? 0 : s.cal_data().serial,
+                    s == null ? null : s.cal_data().callsign,
+                    s == null ? 0.0 : s.frequency);
+       }
+
+       /* CharSequence */
+       public char charAt(int index) {
+               return display.charAt(index);
+       }
+
+       public int length() {
+               return display.length();
+       }
+
+       public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException {
+               return display.subSequence(start, end);
+       }
+
+       public String toString() {
+               return display.toString();
+       }
+
+       /* Comparable */
+       public int compareTo (Object other) {
+               Tracker o = (Tracker) other;
+               if (frequency == 0.0) {
+                       if (o.frequency == 0.0)
+                               return 0;
+                       return -1;
+               }
+               if (o.frequency == 0.0)
+                       return 1;
+
+               int     a = serial - o.serial;
+               int     b = call.compareTo(o.call);
+               int     c = (int) Math.signum(frequency - o.frequency);
+
+               if (b != 0)
+                       return b;
+               if (c != 0)
+                       return c;
+               return a;
+       }
+}
+
+public class AltosDroid extends FragmentActivity implements AltosUnitsListener, LocationListener {
+
+       // Actions sent to the telemetry server at startup time
+
+       public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH";
+       public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB";
+
+       // Message types received by our Handler
+
+       public static final int MSG_STATE           = 1;
+       public static final int MSG_UPDATE_AGE      = 2;
+       public static final int MSG_IDLE_MODE       = 3;
+       public static final int MSG_IGNITER_STATUS  = 4;
+
+       // Intent request codes
+       public static final int REQUEST_CONNECT_DEVICE = 1;
+       public static final int REQUEST_ENABLE_BT      = 2;
+       public static final int REQUEST_PRELOAD_MAPS   = 3;
+       public static final int REQUEST_IDLE_MODE      = 5;
+       public static final int REQUEST_IGNITERS       = 6;
+       public static final int REQUEST_SETUP          = 7;
+
+       public static final String EXTRA_IDLE_MODE = "idle_mode";
+       public static final String EXTRA_IDLE_RESULT = "idle_result";
+       public static final String EXTRA_TELEMETRY_SERVICE = "telemetry_service";
+
+       // Setup result bits
+       public static final int SETUP_BAUD = 1;
+       public static final int SETUP_UNITS = 2;
+       public static final int SETUP_MAP_SOURCE = 4;
+       public static final int SETUP_MAP_TYPE = 8;
+
+       public static FragmentManager   fm;
+
+       private BluetoothAdapter mBluetoothAdapter = null;
+
+       // Flight state values
+       private TextView mCallsignView;
+       private TextView mRSSIView;
+       private TextView mSerialView;
+       private TextView mFlightView;
+       private RelativeLayout mStateLayout;
+       private TextView mStateView;
+       private TextView mAgeView;
+       private boolean  mAgeViewOld;
+       private int mAgeNewColor;
+       private int mAgeOldColor;
+
+       public static final String      tab_pad_name = "pad";
+       public static final String      tab_flight_name = "flight";
+       public static final String      tab_recover_name = "recover";
+       public static final String      tab_map_name = "map";
+
+       // field to display the version at the bottom of the screen
+       private TextView mVersion;
+
+       private boolean idle_mode = false;
+
+       public Location location = null;
+
+       private AltosState      state;
+       private SavedState      saved_state;
+
+       // Tabs
+       TabHost         mTabHost;
+       AltosViewPager  mViewPager;
+       TabsAdapter     mTabsAdapter;
+       ArrayList<AltosDroidTab> mTabs = new ArrayList<AltosDroidTab>();
+       int             tabHeight;
+
+       // Timer and Saved flight state for Age calculation
+       private Timer timer;
+
+       TelemetryState  telemetry_state;
+       Tracker[]       trackers;
+
+
+       UsbDevice       pending_usb_device;
+       boolean         start_with_usb;
+
+       // Service
+       private boolean mIsBound   = false;
+       private Messenger mService = null;
+       final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+       // Text to Speech
+       private AltosVoice mAltosVoice = null;
+
+       // The Handler that gets information back from the Telemetry Service
+       static class IncomingHandler extends Handler {
+               private final WeakReference<AltosDroid> mAltosDroid;
+               IncomingHandler(AltosDroid ad) { mAltosDroid = new WeakReference<AltosDroid>(ad); }
+
+               @Override
+               public void handleMessage(Message msg) {
+                       AltosDroid ad = mAltosDroid.get();
+
+                       switch (msg.what) {
+                       case MSG_STATE:
+                               if (msg.obj == null) {
+                                       AltosDebug.debug("telemetry_state null!");
+                                       return;
+                               }
+                               ad.update_state((TelemetryState) msg.obj);
+                               break;
+                       case MSG_UPDATE_AGE:
+                               ad.update_age();
+                               break;
+                       case MSG_IDLE_MODE:
+                               ad.idle_mode = (Boolean) msg.obj;
+                               ad.update_state(null);
+                               break;
+                       }
+               }
+       };
+
+
+       private ServiceConnection mConnection = new ServiceConnection() {
+               public void onServiceConnected(ComponentName className, IBinder service) {
+                       mService = new Messenger(service);
+                       try {
+                               Message msg = Message.obtain(null, TelemetryService.MSG_REGISTER_CLIENT);
+                               msg.replyTo = mMessenger;
+                               mService.send(msg);
+                       } catch (RemoteException e) {
+                               // In this case the service has crashed before we could even do anything with it
+                       }
+                       if (pending_usb_device != null) {
+                               try {
+                                       mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device));
+                                       pending_usb_device = null;
+                               } catch (RemoteException e) {
+                               }
+                       }
+               }
+
+               public void onServiceDisconnected(ComponentName className) {
+                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
+                       mService = null;
+               }
+       };
+
+       void doBindService() {
+               bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE);
+               mIsBound = true;
+       }
+
+       void doUnbindService() {
+               if (mIsBound) {
+                       // If we have received the service, and hence registered with it, then now is the time to unregister.
+                       if (mService != null) {
+                               try {
+                                       Message msg = Message.obtain(null, TelemetryService.MSG_UNREGISTER_CLIENT);
+                                       msg.replyTo = mMessenger;
+                                       mService.send(msg);
+                               } catch (RemoteException e) {
+                                       // There is nothing special we need to do if the service has crashed.
+                               }
+                       }
+                       // Detach our existing connection.
+                       unbindService(mConnection);
+                       mIsBound = false;
+               }
+       }
+
+       public void registerTab(AltosDroidTab mTab) {
+               mTabs.add(mTab);
+       }
+
+       public void unregisterTab(AltosDroidTab mTab) {
+               mTabs.remove(mTab);
+       }
+
+       public void units_changed(boolean imperial_units) {
+               for (AltosDroidTab mTab : mTabs)
+                       mTab.units_changed(imperial_units);
+       }
+
+       void update_title(TelemetryState telemetry_state) {
+               switch (telemetry_state.connect) {
+               case TelemetryState.CONNECT_CONNECTED:
+                       if (telemetry_state.config != null) {
+                               String str = String.format("S/N %d %6.3f MHz%s", telemetry_state.config.serial,
+                                                          telemetry_state.frequency, idle_mode ? " (idle)" : "");
+                               if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
+                                       str = str.concat(String.format(" %d bps",
+                                                                      AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
+                               setTitle(str);
+                       } else {
+                               setTitle(R.string.title_connected_to);
+                       }
+                       break;
+               case TelemetryState.CONNECT_CONNECTING:
+                       if (telemetry_state.address != null)
+                               setTitle(String.format("Connecting to %s...", telemetry_state.address.name));
+                       else
+                               setTitle("Connecting to something...");
+                       break;
+               case TelemetryState.CONNECT_DISCONNECTED:
+               case TelemetryState.CONNECT_NONE:
+                       setTitle(R.string.title_not_connected);
+                       break;
+               }
+       }
+
+       void start_timer() {
+               if (timer == null) {
+                       timer = new Timer();
+                       timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 1000L);
+               }
+       }
+
+       void stop_timer() {
+               if (timer != null) {
+                       timer.cancel();
+                       timer.purge();
+                       timer = null;
+               }
+       }
+
+       int     selected_serial = 0;
+       int     current_serial;
+       long    switch_time;
+
+       void set_switch_time() {
+               switch_time = System.currentTimeMillis();
+               selected_serial = 0;
+       }
+
+       boolean registered_units_listener;
+
+       void update_state(TelemetryState new_telemetry_state) {
+
+               if (new_telemetry_state != null)
+                       telemetry_state = new_telemetry_state;
+
+               if (selected_serial != 0)
+                       current_serial = selected_serial;
+
+               if (current_serial == 0)
+                       current_serial = telemetry_state.latest_serial;
+
+               if (!registered_units_listener) {
+                       registered_units_listener = true;
+                       AltosPreferences.register_units_listener(this);
+               }
+
+               int     num_trackers = 0;
+               for (AltosState s : telemetry_state.states.values()) {
+                       num_trackers++;
+               }
+
+               trackers = new Tracker[num_trackers + 1];
+
+               int n = 0;
+               trackers[n++] = new Tracker(0, "auto", 0.0);
+
+               for (AltosState s : telemetry_state.states.values())
+                       trackers[n++] = new Tracker(s);
+
+               Arrays.sort(trackers);
+
+               update_title(telemetry_state);
+
+               AltosState      state = null;
+               boolean         aged = true;
+
+               if (telemetry_state.states.containsKey(current_serial)) {
+                       state = telemetry_state.states.get(current_serial);
+                       int age = state_age(state.received_time);
+                       if (age < 20)
+                               aged = false;
+                       if (current_serial == selected_serial)
+                               aged = false;
+                       else if (switch_time != 0 && (switch_time - state.received_time) > 0)
+                               aged = true;
+               }
+
+               if (aged) {
+                       AltosState      newest_state = null;
+                       int             newest_age = 0;
+
+                       for (int serial : telemetry_state.states.keySet()) {
+                               AltosState      existing = telemetry_state.states.get(serial);
+                               int             existing_age = state_age(existing.received_time);
+
+                               if (newest_state == null || existing_age < newest_age) {
+                                       newest_state = existing;
+                                       newest_age = existing_age;
+                               }
+                       }
+
+                       if (newest_state != null)
+                               state = newest_state;
+               }
+
+               update_ui(telemetry_state, state, telemetry_state.quiet);
+
+               start_timer();
+       }
+
+       boolean same_string(String a, String b) {
+               if (a == null) {
+                       if (b == null)
+                               return true;
+                       return false;
+               } else {
+                       if (b == null)
+                               return false;
+                       return a.equals(b);
+               }
+       }
+
+
+       private int blend_component(int a, int b, double r, int shift, int mask) {
+               return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
+       }
+       private int blend_color(int a, int b, double r) {
+               return (blend_component(a, b, r, 0, 0xff) |
+                       blend_component(a, b, r, 8, 0xff) |
+                       blend_component(a, b, r, 16, 0xff) |
+                       blend_component(a, b, r, 24, 0xff));
+       }
+
+       int state_age(long received_time) {
+               return (int) ((System.currentTimeMillis() - received_time + 500) / 1000);
+       }
+
+       void set_screen_on(int age) {
+               if (age < 60)
+                       getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+               else
+                       getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+       }
+
+       void update_age() {
+               if (saved_state != null) {
+                       int age = state_age(saved_state.received_time);
+
+                       double age_scale = age / 100.0;
+
+                       if (age_scale > 1.0)
+                               age_scale = 1.0;
+
+                       mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
+
+                       set_screen_on(age);
+
+                       String  text;
+                       if (age < 60)
+                               text = String.format("%ds", age);
+                       else if (age < 60 * 60)
+                               text = String.format("%dm", age / 60);
+                       else if (age < 60 * 60 * 24)
+                               text = String.format("%dh", age / (60 * 60));
+                       else
+                               text = String.format("%dd", age / (24 * 60 * 60));
+                       mAgeView.setText(text);
+               }
+       }
+
+       void update_ui(TelemetryState telem_state, AltosState state, boolean quiet) {
+
+               this.state = state;
+
+               int prev_state = AltosLib.ao_flight_invalid;
+
+               AltosGreatCircle from_receiver = null;
+
+               if (saved_state != null)
+                       prev_state = saved_state.state;
+
+               if (state != null) {
+                       set_screen_on(state_age(state.received_time));
+
+                       if (state.state() == AltosLib.ao_flight_stateless) {
+                               boolean prev_locked = false;
+                               boolean locked = false;
+
+                               if(state.gps != null)
+                                       locked = state.gps.locked;
+                               if (saved_state != null)
+                                       prev_locked = saved_state.locked;
+                               if (prev_locked != locked) {
+                                       String currentTab = mTabHost.getCurrentTabTag();
+                                       if (locked) {
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
+                                       } else {
+                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_pad_name);
+                                       }
+                               }
+                       } else {
+                               if (prev_state != state.state()) {
+                                       String currentTab = mTabHost.getCurrentTabTag();
+                                       switch (state.state()) {
+                                       case AltosLib.ao_flight_boost:
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
+                                               break;
+                                       case AltosLib.ao_flight_landed:
+                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_recover_name);
+                                               break;
+                                       case AltosLib.ao_flight_stateless:
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (location != null && state.gps != null && state.gps.locked) {
+                               double altitude = 0;
+                               if (location.hasAltitude())
+                                       altitude = location.getAltitude();
+                               from_receiver = new AltosGreatCircle(location.getLatitude(),
+                                                                    location.getLongitude(),
+                                                                    altitude,
+                                                                    state.gps.lat,
+                                                                    state.gps.lon,
+                                                                    state.gps.alt);
+                       }
+
+                       if (saved_state == null || !same_string(saved_state.callsign, state.cal_data().callsign)) {
+                               mCallsignView.setText(state.cal_data().callsign);
+                       }
+                       if (saved_state == null || state.cal_data().serial != saved_state.serial) {
+                               if (state.cal_data().serial == AltosLib.MISSING)
+                                       mSerialView.setText("");
+                               else
+                                       mSerialView.setText(String.format("%d", state.cal_data().serial));
+                       }
+                       if (saved_state == null || state.cal_data().flight != saved_state.flight) {
+                               if (state.cal_data().flight == AltosLib.MISSING)
+                                       mFlightView.setText("");
+                               else
+                                       mFlightView.setText(String.format("%d", state.cal_data().flight));
+                       }
+                       if (saved_state == null || state.state() != saved_state.state) {
+                               if (state.state() == AltosLib.ao_flight_stateless) {
+                                       mStateLayout.setVisibility(View.GONE);
+                               } else {
+                                       mStateView.setText(state.state_name());
+                                       mStateLayout.setVisibility(View.VISIBLE);
+                               }
+                       }
+                       if (saved_state == null || state.rssi != saved_state.rssi) {
+                               if (state.rssi == AltosLib.MISSING)
+                                       mRSSIView.setText("");
+                               else
+                                       mRSSIView.setText(String.format("%d", state.rssi));
+                       }
+                       saved_state = new SavedState(state);
+               }
+
+               for (AltosDroidTab mTab : mTabs)
+                       mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
+
+               AltosDebug.debug("quiet %b\n", quiet);
+               if (mAltosVoice != null)
+                       mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem(), quiet);
+
+       }
+
+       private void onTimerTick() {
+               try {
+                       mMessenger.send(Message.obtain(null, MSG_UPDATE_AGE));
+               } catch (RemoteException e) {
+               }
+       }
+
+       static String pos(double p, String pos, String neg) {
+               String  h = pos;
+               if (p == AltosLib.MISSING)
+                       return "";
+               if (p < 0) {
+                       h = neg;
+                       p = -p;
+               }
+               int deg = (int) Math.floor(p);
+               double min = (p - Math.floor(p)) * 60.0;
+               return String.format("%d°%9.4f\" %s", deg, min, h);
+       }
+
+       static String number(String format, double value) {
+               if (value == AltosLib.MISSING)
+                       return "";
+               return String.format(format, value);
+       }
+
+       static String integer(String format, int value) {
+               if (value == AltosLib.MISSING)
+                       return "";
+               return String.format(format, value);
+       }
+
+       private View create_tab_view(String label) {
+               LayoutInflater inflater = (LayoutInflater) this.getLayoutInflater();
+               View tab_view = inflater.inflate(R.layout.tab_layout, null);
+               TextView text_view = (TextView) tab_view.findViewById (R.id.tabLabel);
+               text_view.setText(label);
+               return tab_view;
+       }
+
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+               AltosDebug.init(this);
+               AltosDebug.debug("+++ ON CREATE +++");
+
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
+
+               fm = getSupportFragmentManager();
+
+               // Set up the window layout
+               setContentView(R.layout.altosdroid);
+
+               // Create the Tabs and ViewPager
+               mTabHost = (TabHost)findViewById(android.R.id.tabhost);
+               mTabHost.setup();
+
+               mViewPager = (AltosViewPager)findViewById(R.id.pager);
+               mViewPager.setOffscreenPageLimit(4);
+
+               mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
+
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null);
+
+               // Display the Version
+               mVersion = (TextView) findViewById(R.id.version);
+               mVersion.setText("Version: " + BuildInfo.version +
+                                " Built: " + BuildInfo.builddate + " " + BuildInfo.buildtime + " " + BuildInfo.buildtz +
+                                " (" + BuildInfo.branch + "-" + BuildInfo.commitnum + "-" + BuildInfo.commithash + ")");
+
+               mCallsignView  = (TextView) findViewById(R.id.callsign_value);
+               mRSSIView      = (TextView) findViewById(R.id.rssi_value);
+               mSerialView    = (TextView) findViewById(R.id.serial_value);
+               mFlightView    = (TextView) findViewById(R.id.flight_value);
+               mStateLayout   = (RelativeLayout) findViewById(R.id.state_container);
+               mStateView     = (TextView) findViewById(R.id.state_value);
+               mAgeView       = (TextView) findViewById(R.id.age_value);
+               mAgeNewColor   = mAgeView.getTextColors().getDefaultColor();
+               mAgeOldColor   = getResources().getColor(R.color.old_color);
+       }
+
+       private void ensureBluetooth() {
+               // Get local Bluetooth adapter
+               mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+               /* if there is a BT adapter and it isn't turned on, then turn it on */
+               if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
+                       Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+                       startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
+               }
+       }
+
+       private boolean check_usb() {
+               UsbDevice       device = AltosUsb.find_device(this, AltosLib.product_basestation);
+
+               if (device != null) {
+                       Intent          i = new Intent(this, AltosDroid.class);
+                       PendingIntent   pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0);
+
+                       if (AltosUsb.request_permission(this, device, pi)) {
+                               connectUsb(device);
+                       }
+                       start_with_usb = true;
+                       return true;
+               }
+
+               start_with_usb = false;
+
+               return false;
+       }
+
+       private void noticeIntent(Intent intent) {
+
+               /* Ok, this is pretty convenient.
+                *
+                * When a USB device is plugged in, and our 'hotplug'
+                * intent registration fires, we get an Intent with
+                * EXTRA_DEVICE set.
+                *
+                * When we start up and see a usb device and request
+                * permission to access it, that queues a
+                * PendingIntent, which has the EXTRA_DEVICE added in,
+                * along with the EXTRA_PERMISSION_GRANTED field as
+                * well.
+                *
+                * So, in both cases, we get the device name using the
+                * same call. We check to see if access was granted,
+                * in which case we ignore the device field and do our
+                * usual startup thing.
+                */
+
+               UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+               boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+
+               AltosDebug.debug("intent %s device %s granted %s", intent, device, granted);
+
+               if (!granted)
+                       device = null;
+
+               if (device != null) {
+                       AltosDebug.debug("intent has usb device " + device.toString());
+                       connectUsb(device);
+               } else {
+
+                       /* 'granted' is only false if this intent came
+                        * from the request_permission call and
+                        * permission was denied. In which case, we
+                        * don't want to loop forever...
+                        */
+                       if (granted) {
+                               AltosDebug.debug("check for a USB device at startup");
+                               if (check_usb())
+                                       return;
+                       }
+                       AltosDebug.debug("Starting by looking for bluetooth devices");
+                       ensureBluetooth();
+               }
+       }
+
+       @Override
+       public void onStart() {
+               super.onStart();
+               AltosDebug.debug("++ ON START ++");
+
+               set_switch_time();
+
+               noticeIntent(getIntent());
+
+               // Start Telemetry Service
+               String  action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH;
+
+               startService(new Intent(action, null, AltosDroid.this, TelemetryService.class));
+
+               doBindService();
+
+               if (mAltosVoice == null)
+                       mAltosVoice = new AltosVoice(this);
+
+       }
+
+       @Override
+       public void onNewIntent(Intent intent) {
+               super.onNewIntent(intent);
+               AltosDebug.debug("onNewIntent");
+               noticeIntent(intent);
+       }
+
+       @Override
+       public void onResume() {
+               super.onResume();
+               AltosDebug.debug("+ ON RESUME +");
+
+               // Listen for GPS and Network position updates
+               LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+               locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
+
+               location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+
+               if (location != null)
+                       AltosDebug.debug("Resume, location is %f,%f\n",
+                                        location.getLatitude(),
+                                        location.getLongitude());
+
+               update_ui(telemetry_state, state, true);
+       }
+
+       @Override
+       public void onPause() {
+               super.onPause();
+               AltosDebug.debug("- ON PAUSE -");
+               // Stop listening for location updates
+               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+       }
+
+       @Override
+       public void onStop() {
+               super.onStop();
+               AltosDebug.debug("-- ON STOP --");
+       }
+
+       @Override
+       public void onDestroy() {
+               super.onDestroy();
+               AltosDebug.debug("--- ON DESTROY ---");
+
+               doUnbindService();
+               if (mAltosVoice != null) {
+                       mAltosVoice.stop();
+                       mAltosVoice = null;
+               }
+               stop_timer();
+       }
+
+       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+               AltosDebug.debug("onActivityResult " + resultCode);
+               switch (requestCode) {
+               case REQUEST_CONNECT_DEVICE:
+                       // When DeviceListActivity returns with a device to connect to
+                       if (resultCode == Activity.RESULT_OK) {
+                               connectDevice(data);
+                       }
+                       break;
+               case REQUEST_ENABLE_BT:
+                       // When the request to enable Bluetooth returns
+                       if (resultCode == Activity.RESULT_OK) {
+                               // Bluetooth is now enabled, so set up a chat session
+                               //setupChat();
+                               AltosDebug.debug("BT enabled");
+                               bluetoothEnabled(data);
+                       } else {
+                               // User did not enable Bluetooth or an error occured
+                               AltosDebug.debug("BT not enabled");
+                       }
+                       break;
+               case REQUEST_IDLE_MODE:
+                       if (resultCode == Activity.RESULT_OK)
+                               idle_mode(data);
+                       break;
+               case REQUEST_IGNITERS:
+                       break;
+               case REQUEST_SETUP:
+                       if (resultCode == Activity.RESULT_OK)
+                               note_setup_changes(data);
+                       break;
+               }
+       }
+
+       private void note_setup_changes(Intent data) {
+               int changes = data.getIntExtra(SetupActivity.EXTRA_SETUP_CHANGES, 0);
+
+               if ((changes & SETUP_BAUD) != 0) {
+                       try {
+                               mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD,
+                                                            AltosPreferences.telemetry_rate(1)));
+                       } catch (RemoteException re) {
+                       }
+               }
+               if ((changes & SETUP_UNITS) != 0) {
+                       /* nothing to do here */
+               }
+               if ((changes & SETUP_MAP_SOURCE) != 0) {
+                       /* nothing to do here */
+               }
+               if ((changes & SETUP_MAP_TYPE) != 0) {
+                       /* nothing to do here */
+               }
+               set_switch_time();
+       }
+
+       private void connectUsb(UsbDevice device) {
+               if (mService == null)
+                       pending_usb_device = device;
+               else {
+                       // Attempt to connect to the device
+                       try {
+                               mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device));
+                               AltosDebug.debug("Sent OPEN_USB message");
+                       } catch (RemoteException e) {
+                               AltosDebug.debug("connect device message failed");
+                       }
+               }
+       }
+
+       private void bluetoothEnabled(Intent data) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_BLUETOOTH_ENABLED, null));
+               } catch (RemoteException e) {
+                       AltosDebug.debug("send BT enabled message failed");
+               }
+       }
+
+       private void connectDevice(Intent data) {
+               // Attempt to connect to the device
+               try {
+                       String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+                       String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME);
+
+                       AltosDebug.debug("Connecting to " + address + " " + name);
+                       DeviceAddress   a = new DeviceAddress(address, name);
+                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a));
+                       AltosDebug.debug("Sent connecting message");
+               } catch (RemoteException e) {
+                       AltosDebug.debug("connect device message failed");
+               }
+       }
+
+       private void disconnectDevice(boolean remember) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_DISCONNECT, (Boolean) remember));
+               } catch (RemoteException e) {
+               }
+       }
+
+       private void idle_mode(Intent data) {
+               int type = data.getIntExtra(IdleModeActivity.EXTRA_IDLE_RESULT, -1);
+               Message msg;
+
+               AltosDebug.debug("intent idle_mode %d", type);
+               switch (type) {
+               case IdleModeActivity.IDLE_MODE_CONNECT:
+                       msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_START);
+                       try {
+                               mService.send(msg);
+                       } catch (RemoteException re) {
+                       }
+                       break;
+               case IdleModeActivity.IDLE_MODE_DISCONNECT:
+                       msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_STOP);
+                       try {
+                               mService.send(msg);
+                       } catch (RemoteException re) {
+                       }
+                       break;
+               case IdleModeActivity.IDLE_MODE_REBOOT:
+                       msg = Message.obtain(null, TelemetryService.MSG_REBOOT);
+                       try {
+                               mService.send(msg);
+                       } catch (RemoteException re) {
+                       }
+                       break;
+               case IdleModeActivity.IDLE_MODE_IGNITERS:
+                       Intent serverIntent = new Intent(this, IgniterActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_IGNITERS);
+                       break;
+               }
+       }
+
+       @Override
+       public boolean onCreateOptionsMenu(Menu menu) {
+               MenuInflater inflater = getMenuInflater();
+               inflater.inflate(R.menu.option_menu, menu);
+               return true;
+       }
+
+       void setFrequency(double freq) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
+                       set_switch_time();
+               } catch (RemoteException e) {
+               }
+       }
+
+       void setFrequency(AltosFrequency frequency) {
+               setFrequency (frequency.frequency);
+       }
+
+       void setBaud(int baud) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
+                       set_switch_time();
+               } catch (RemoteException e) {
+               }
+       }
+
+       void setBaud(String baud) {
+               try {
+                       int     value = Integer.parseInt(baud);
+                       int     rate = AltosLib.ao_telemetry_rate_38400;
+                       switch (value) {
+                       case 2400:
+                               rate = AltosLib.ao_telemetry_rate_2400;
+                               break;
+                       case 9600:
+                               rate = AltosLib.ao_telemetry_rate_9600;
+                               break;
+                       case 38400:
+                               rate = AltosLib.ao_telemetry_rate_38400;
+                               break;
+                       }
+                       setBaud(rate);
+               } catch (NumberFormatException e) {
+               }
+       }
+
+       void select_tracker(int serial) {
+               int i;
+
+               AltosDebug.debug("select tracker %d\n", serial);
+
+               if (serial == selected_serial) {
+                       AltosDebug.debug("%d already selected\n", serial);
+                       return;
+               }
+
+               if (serial != 0) {
+                       for (i = 0; i < trackers.length; i++)
+                               if (trackers[i].serial == serial)
+                                       break;
+
+                       if (i == trackers.length) {
+                               AltosDebug.debug("attempt to select unknown tracker %d\n", serial);
+                               return;
+                       }
+               }
+
+               current_serial = selected_serial = serial;
+               update_state(null);
+       }
+
+       void touch_trackers(Integer[] serials) {
+               AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this);
+               builder_tracker.setTitle("Select Tracker");
+
+               final Tracker[] my_trackers = new Tracker[serials.length + 1];
+
+               my_trackers[0] = new Tracker(null);
+
+               for (int i = 0; i < serials.length; i++) {
+                       AltosState      s = telemetry_state.states.get(serials[i]);
+                       my_trackers[i+1] = new Tracker(s);
+               }
+               builder_tracker.setItems(my_trackers,
+                                        new DialogInterface.OnClickListener() {
+                                                public void onClick(DialogInterface dialog, int item) {
+                                                        if (item == 0)
+                                                                select_tracker(0);
+                                                        else
+                                                                select_tracker(my_trackers[item].serial);
+                                                }
+                                        });
+               AlertDialog alert_tracker = builder_tracker.create();
+               alert_tracker.show();
+       }
+
+       void delete_track(int serial) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
+               } catch (Exception ex) {
+               }
+       }
+
+       @Override
+       public boolean onOptionsItemSelected(MenuItem item) {
+               Intent serverIntent = null;
+               switch (item.getItemId()) {
+               case R.id.connect_scan:
+                       ensureBluetooth();
+                       // Launch the DeviceListActivity to see devices and do scan
+                       serverIntent = new Intent(this, DeviceListActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+                       return true;
+               case R.id.disconnect:
+                       /* Disconnect the device
+                        */
+                       disconnectDevice(false);
+                       return true;
+               case R.id.quit:
+                       AltosDebug.debug("R.id.quit");
+                       disconnectDevice(true);
+                       finish();
+                       return true;
+               case R.id.setup:
+                       serverIntent = new Intent(this, SetupActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_SETUP);
+                       return true;
+               case R.id.select_freq:
+                       // Set the TBT radio frequency
+
+                       final AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
+                       String[] frequency_strings = new String[frequencies.length];
+                       for (int i = 0; i < frequencies.length; i++)
+                               frequency_strings[i] = frequencies[i].toString();
+
+                       AlertDialog.Builder builder_freq = new AlertDialog.Builder(this);
+                       builder_freq.setTitle("Pick a frequency");
+                       builder_freq.setItems(frequency_strings,
+                                        new DialogInterface.OnClickListener() {
+                                                public void onClick(DialogInterface dialog, int item) {
+                                                        setFrequency(frequencies[item]);
+                                                }
+                                        });
+                       AlertDialog alert_freq = builder_freq.create();
+                       alert_freq.show();
+                       return true;
+               case R.id.select_tracker:
+                       if (trackers != null) {
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Select a tracker");
+                               builder_serial.setItems(trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       System.out.printf("select item %d %s\n", item, trackers[item].display);
+                                                                       if (item == 0)
+                                                                               select_tracker(0);
+                                                                       else
+                                                                               select_tracker(trackers[item].serial);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
+               case R.id.delete_track:
+                       if (trackers != null) {
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Delete a track");
+                               final Tracker[] my_trackers = new Tracker[trackers.length - 1];
+                               for (int i = 0; i < trackers.length - 1; i++)
+                                       my_trackers[i] = trackers[i+1];
+                               builder_serial.setItems(my_trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       delete_track(my_trackers[item].serial);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
+               case R.id.idle_mode:
+                       serverIntent = new Intent(this, IdleModeActivity.class);
+                       serverIntent.putExtra(EXTRA_IDLE_MODE, idle_mode);
+                       startActivityForResult(serverIntent, REQUEST_IDLE_MODE);
+                       return true;
+               }
+               return false;
+       }
+
+       static String direction(AltosGreatCircle from_receiver,
+                               Location receiver) {
+               if (from_receiver == null)
+                       return null;
+
+               if (receiver == null)
+                       return null;
+
+               if (!receiver.hasBearing())
+                       return null;
+
+               float   bearing = receiver.getBearing();
+               float   heading = (float) from_receiver.bearing - bearing;
+
+               while (heading <= -180.0f)
+                       heading += 360.0f;
+               while (heading > 180.0f)
+                       heading -= 360.0f;
+
+               int iheading = (int) (heading + 0.5f);
+
+               if (-1 < iheading && iheading < 1)
+                       return "ahead";
+               else if (iheading < -179 || 179 < iheading)
+                       return "backwards";
+               else if (iheading < 0)
+                       return String.format("left %d°", -iheading);
+               else
+                       return String.format("right %d°", iheading);
+       }
+
+       public void onLocationChanged(Location location) {
+               this.location = location;
+               AltosDebug.debug("Location changed to %f,%f",
+                                location.getLatitude(),
+                                location.getLongitude());
+               update_ui(telemetry_state, state, false);
+       }
+
+       public void onStatusChanged(String provider, int status, Bundle extras) {
+               AltosDebug.debug("Location status now %d\n", status);
+       }
+
+       public void onProviderEnabled(String provider) {
+               AltosDebug.debug("Location provider enabled %s\n", provider);
+       }
+
+       public void onProviderDisabled(String provider) {
+               AltosDebug.debug("Location provider disabled %s\n", provider);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidLink.java
new file mode 100644 (file)
index 0000000..a443b6e
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_13.*;
+
+public abstract class AltosDroidLink extends AltosLink {
+
+       Handler         handler;
+
+       Thread          input_thread   = null;
+
+       public double frequency() {
+               return frequency;
+       }
+
+       public int telemetry_rate() {
+               return telemetry_rate;
+       }
+
+       public void save_frequency() {
+               AltosPreferences.set_frequency(0, frequency);
+       }
+
+       public void save_telemetry_rate() {
+               AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+       }
+
+       Object closed_lock = new Object();
+       boolean closing = false;
+       boolean closed = false;
+
+       public boolean closed() {
+               synchronized(closed_lock) {
+                       return closing;
+               }
+       }
+
+       void connected() throws InterruptedException {
+               input_thread = new Thread(this);
+               input_thread.start();
+
+               // Configure the newly connected device for telemetry
+               print("~\nE 0\n");
+               set_monitor(false);
+               AltosDebug.debug("ConnectThread: connected");
+
+               /* Let TelemetryService know we're connected
+                */
+               handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
+
+               /* Notify other waiting threads that we're connected now
+                */
+               notifyAll();
+       }
+
+       public void closing() {
+               synchronized(closed_lock) {
+                       AltosDebug.debug("Marked closing true");
+                       closing = true;
+               }
+       }
+
+       private boolean actually_closed() {
+               synchronized(closed_lock) {
+                       return closed;
+               }
+       }
+
+       abstract void close_device();
+
+       public void close() {
+               AltosDebug.debug("close(): begin");
+
+               closing();
+
+               flush_output();
+
+               synchronized (closed_lock) {
+                       AltosDebug.debug("Marked closed true");
+                       closed = true;
+               }
+
+               close_device();
+
+               synchronized(this) {
+
+                       if (input_thread != null) {
+                               AltosDebug.debug("close(): stopping input_thread");
+                               try {
+                                       AltosDebug.debug("close(): input_thread.interrupt().....");
+                                       input_thread.interrupt();
+                                       AltosDebug.debug("close(): input_thread.join().....");
+                                       input_thread.join();
+                               } catch (Exception e) {}
+                               input_thread = null;
+                       }
+                       notifyAll();
+               }
+       }
+
+       abstract int write(byte[] buffer, int len);
+
+       abstract int read(byte[] buffer, int len);
+
+       private static final int buffer_size = 64;
+
+       private byte[] in_buffer = new byte[buffer_size];
+       private byte[] out_buffer = new byte[buffer_size];
+       private int buffer_len = 0;
+       private int buffer_off = 0;
+       private int out_buffer_off = 0;
+
+       private byte[] debug_chars = new byte[buffer_size];
+       private int debug_off;
+
+       private void debug_input(byte b) {
+               if (b == '\n') {
+                       AltosDebug.debug("            " + new String(debug_chars, 0, debug_off));
+                       debug_off = 0;
+               } else {
+                       if (debug_off < buffer_size)
+                               debug_chars[debug_off++] = b;
+               }
+       }
+
+       private void disconnected() {
+               if (closed()) {
+                       AltosDebug.debug("disconnected after closed");
+                       return;
+               }
+
+               AltosDebug.debug("Sending disconnected message");
+               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+       }
+
+       public int getchar() {
+
+               if (actually_closed())
+                       return ERROR;
+
+               while (buffer_off == buffer_len) {
+                       buffer_len = read(in_buffer, buffer_size);
+                       if (buffer_len < 0) {
+                               AltosDebug.debug("ERROR returned from getchar()");
+                               disconnected();
+                               return ERROR;
+                       }
+                       buffer_off = 0;
+               }
+//             if (AltosDebug.D)
+//                     debug_input(in_buffer[buffer_off]);
+               return in_buffer[buffer_off++];
+       }
+
+       public void flush_output() {
+               super.flush_output();
+
+               if (actually_closed()) {
+                       out_buffer_off = 0;
+                       return;
+               }
+
+               while (out_buffer_off != 0) {
+                       int     sent = write(out_buffer, out_buffer_off);
+
+                       if (sent <= 0) {
+                               AltosDebug.debug("flush_output() failed");
+                               out_buffer_off = 0;
+                               break;
+                       }
+
+                       if (sent < out_buffer_off)
+                               System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
+
+                       out_buffer_off -= sent;
+               }
+       }
+
+       public void putchar(byte c) {
+               out_buffer[out_buffer_off++] = c;
+               if (out_buffer_off == buffer_size)
+                       flush_output();
+       }
+
+       public void print(String data) {
+               byte[] bytes = data.getBytes();
+//             AltosDebug.debug(data.replace('\n', '\\'));
+               for (byte b : bytes)
+                       putchar(b);
+       }
+
+       public AltosDroidLink(Handler handler) {
+               this.handler = handler;
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java
new file mode 100644 (file)
index 0000000..6da90a3
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+import android.location.Location;
+import org.altusmetrum.altoslib_13.*;
+
+public interface AltosDroidMapInterface {
+       public void onCreateView(AltosDroid altos_droid);
+
+       public void onDestroyView();
+
+       public void set_visible(boolean visible);
+
+       public void center(double lat, double lon, double accuracy);
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java
new file mode 100644 (file)
index 0000000..294094c
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+public interface AltosDroidMapSourceListener {
+       public void map_source_changed(int map_source);
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java
new file mode 100644 (file)
index 0000000..8bb78c0
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+package org.altusmetrum.AltosDroid;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+import android.content.Context;
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosDroidPreferences extends AltosPreferences {
+
+       /* Active device preference name */
+       final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS";
+       final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME";
+
+       static DeviceAddress    active_device_address;
+
+       /* Map source preference name */
+       final static String mapSourcePreference = "MAP-SOURCE";
+
+       static final int        MAP_SOURCE_OFFLINE = 0;
+       static final int        MAP_SOURCE_ONLINE = 1;
+
+       static int      map_source;
+
+       public static void init(Context context) {
+               if (backend != null)
+                       return;
+
+               AltosPreferences.init(new AltosDroidPreferencesBackend(context));
+
+               String address = backend.getString(activeDeviceAddressPreference, null);
+               String name = backend.getString(activeDeviceNamePreference, null);
+
+               if (address != null && name != null)
+                       active_device_address = new DeviceAddress (address, name);
+
+               map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
+       }
+
+       public static void set_active_device(DeviceAddress address) {
+               synchronized(backend) {
+                       active_device_address = address;
+                       if (active_device_address != null) {
+                               backend.putString(activeDeviceAddressPreference, active_device_address.address);
+                               backend.putString(activeDeviceNamePreference, active_device_address.name);
+                       } else {
+                               backend.remove(activeDeviceAddressPreference);
+                               backend.remove(activeDeviceNamePreference);
+                       }
+                       flush_preferences();
+               }
+       }
+
+       public static DeviceAddress active_device() {
+               synchronized(backend) {
+                       return active_device_address;
+               }
+       }
+
+       static LinkedList<AltosDroidMapSourceListener> map_source_listeners;
+
+       public static void set_map_source(int map_source) {
+               synchronized(backend) {
+                       AltosDroidPreferences.map_source = map_source;
+                       backend.putInt(mapSourcePreference, map_source);
+                       flush_preferences();
+               }
+               if (map_source_listeners != null) {
+                       for (AltosDroidMapSourceListener l : map_source_listeners) {
+                               l.map_source_changed(map_source);
+                       }
+               }
+       }
+
+       public static int map_source() {
+               synchronized(backend) {
+                       return map_source;
+               }
+       }
+
+       public static void register_map_source_listener(AltosDroidMapSourceListener l) {
+               synchronized(backend) {
+                       if (map_source_listeners == null)
+                               map_source_listeners = new LinkedList<AltosDroidMapSourceListener>();
+                       map_source_listeners.add(l);
+               }
+       }
+
+       public static void unregister_map_source_listener(AltosDroidMapSourceListener l) {
+               synchronized(backend) {
+                       map_source_listeners.remove(l);
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
new file mode 100644 (file)
index 0000000..854fe86
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.File;
+import java.util.Map;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.util.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosDroidPreferencesBackend extends AltosPreferencesBackend {
+       public final static String        NAME    = "org.altusmetrum.AltosDroid";
+       private Context                   context = null;
+       private SharedPreferences         prefs   = null;
+       private SharedPreferences.Editor  editor  = null;
+
+       public AltosDroidPreferencesBackend(Context in_context) {
+               this(in_context, NAME);
+       }
+
+       public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
+               context = in_context;
+               prefs   = context.getSharedPreferences(in_prefs, 0);
+               editor  = prefs.edit();
+       }
+
+       public String[] keys() {
+               Map<String, ?> all = prefs.getAll();
+               Object[] ao = all.keySet().toArray();
+
+               String[] as = new String[ao.length];
+               for (int i = 0; i < ao.length; i++)
+                       as[i] = (String) ao[i];
+               return as;
+       }
+
+       public AltosPreferencesBackend node(String key) {
+               if (!nodeExists(key))
+                       putBoolean(key, true);
+               return new AltosDroidPreferencesBackend(context, key);
+       }
+
+       public boolean nodeExists(String key) {
+               return prefs.contains(key);
+       }
+
+       public boolean getBoolean(String key, boolean def) {
+               return prefs.getBoolean(key, def);
+       }
+
+       public double getDouble(String key, double def) {
+               Float f = Float.valueOf(prefs.getFloat(key, (float)def));
+               return f.doubleValue();
+       }
+
+       public int getInt(String key, int def) {
+               return prefs.getInt(key, def);
+       }
+
+       public String getString(String key, String def) {
+               String  ret;
+               ret = prefs.getString(key, def);
+//             AltosDebug.debug("AltosDroidPreferencesBackend get string %s:\n", key);
+//             if (ret == null)
+//                     AltosDebug.debug("      (null)\n");
+//             else {
+//                     String[] lines = ret.split("\n");
+//                     for (String l : lines)
+//                             AltosDebug.debug("        %s\n", l);
+//             }
+               return ret;
+       }
+
+       public byte[] getBytes(String key, byte[] def) {
+               String save = prefs.getString(key, null);
+
+               if (save == null)
+                       return def;
+
+               byte[] bytes = Base64.decode(save, Base64.DEFAULT);
+               return bytes;
+       }
+
+       public void putBoolean(String key, boolean value) {
+               editor.putBoolean(key, value);
+       }
+
+       public void putDouble(String key, double value) {
+               editor.putFloat(key, (float)value);
+       }
+
+       public void putInt(String key, int value) {
+               editor.putInt(key, value);
+       }
+
+       public void putString(String key, String value) {
+//             AltosDebug.debug("AltosDroidPreferencesBackend put string %s:\n", key);
+//             String[] lines = value.split("\n");
+//             for (String l : lines)
+//                     AltosDebug.debug("        %s\n", l);
+               editor.putString(key, value);
+       }
+
+       public void putBytes(String key, byte[] bytes) {
+               String save = Base64.encodeToString(bytes, Base64.DEFAULT);
+               editor.putString(key, save);
+       }
+
+       public void remove(String key) {
+               AltosDebug.debug("remove preference %s\n", key);
+               editor.remove(key);
+       }
+
+       public void flush() {
+               editor.apply();
+       }
+
+       public File homeDirectory() {
+               return Environment.getExternalStorageDirectory();
+       }
+
+       public void debug(String format, Object ... arguments) {
+               AltosDebug.debug(format, arguments);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidTab.java
new file mode 100644 (file)
index 0000000..9594f85
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_13.*;
+import android.location.Location;
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.FragmentManager;
+import android.location.Location;
+import android.widget.TextView;
+
+public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
+       TelemetryState          last_telem_state;
+       AltosState              last_state;
+       AltosGreatCircle        last_from_receiver;
+       Location                last_receiver;
+       AltosDroid              altos_droid;
+
+       public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
+
+       public abstract String tab_name();
+
+       public void units_changed(boolean imperial_units) {
+               if (!isHidden())
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
+       }
+
+       public void set_value(TextView text_view,
+                             AltosUnits units,
+                             int width,
+                             double value) {
+               if (value == AltosLib.MISSING)
+                       text_view.setText("");
+               else
+                       text_view.setText(units.show(width, value));
+       }
+
+       public void set_visible(boolean visible) {
+               FragmentTransaction     ft = AltosDroid.fm.beginTransaction();
+               AltosDebug.debug("set visible %b %s\n", visible, tab_name());
+               if (visible) {
+                       ft.show(this);
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
+               } else
+                       ft.hide(this);
+               try {
+                       ft.commitAllowingStateLoss();
+               } catch (IllegalStateException ie) {
+               }
+       }
+
+       @Override
+       public void onAttach(Activity activity) {
+               super.onAttach(activity);
+               altos_droid = (AltosDroid) activity;
+               altos_droid.registerTab(this);
+       }
+
+       @Override
+       public void onDetach() {
+               super.onDetach();
+               altos_droid.unregisterTab(this);
+               altos_droid = null;
+       }
+
+       @Override
+       public void onResume() {
+               super.onResume();
+               AltosDebug.debug("onResume tab %s\n", tab_name());
+               set_visible(true);
+       }
+
+       public void update_ui(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver, boolean is_current)
+       {
+               last_telem_state = telem_state;
+               last_state = state;
+               last_from_receiver = from_receiver;
+               last_receiver = receiver;
+               if (is_current)
+                       show(telem_state, state, from_receiver, receiver);
+               else
+                       return;
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java
new file mode 100644 (file)
index 0000000..1aebcd3
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.*;
+
+class Rocket implements Comparable {
+       AltosLatLon     position;
+       String          name;
+       int             serial;
+       long            last_packet;
+       boolean         active;
+       AltosMapOffline map_offline;
+
+       void paint() {
+               map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
+               map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
+       }
+
+       void set_position(AltosLatLon position, long last_packet) {
+               this.position = position;
+               this.last_packet = last_packet;
+       }
+
+       void set_active(boolean active) {
+               this.active = active;
+       }
+
+       public int compareTo(Object o) {
+               Rocket other = (Rocket) o;
+
+               if (active && !other.active)
+                       return 1;
+               if (other.active && !active)
+                       return -1;
+
+               long    diff = last_packet - other.last_packet;
+
+               if (diff > 0)
+                       return 1;
+               if (diff < 0)
+                       return -1;
+               return 0;
+       }
+
+       Rocket(int serial, AltosMapOffline map_offline) {
+               this.serial = serial;
+               this.name = String.format("%d", serial);
+               this.map_offline = map_offline;
+       }
+}
+
+public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface, AltosMapTypeListener {
+       ScaleGestureDetector    scale_detector;
+       boolean                 scaling;
+       AltosMap                map;
+       AltosDroid              altos_droid;
+
+       static int scale = 1;
+
+       AltosLatLon     here;
+       AltosLatLon     there;
+       AltosLatLon     pad;
+
+       Canvas  canvas;
+       Paint   paint;
+
+       Bitmap  pad_bitmap;
+       int     pad_off_x, pad_off_y;
+       Bitmap  rocket_bitmap;
+       int     rocket_off_x, rocket_off_y;
+       Bitmap  here_bitmap;
+       int     here_off_x, here_off_y;
+
+       static  final int       WHITE = 0xffffffff;
+       static  final int       RED   = 0xffff0000;
+       static  final int       PINK  = 0xffff8080;
+       static  final int       YELLOW= 0xffffff00;
+       static  final int       CYAN  = 0xff00ffff;
+       static  final int       BLUE  = 0xff0000ff;
+       static  final int       BLACK = 0xff000000;
+
+       public static final int stateColors[] = {
+               WHITE,  // startup
+               WHITE,  // idle
+               WHITE,  // pad
+               RED,    // boost
+               PINK,   // fast
+               YELLOW, // coast
+               CYAN,   // drogue
+               BLUE,   // main
+               BLACK,  // landed
+               BLACK,  // invalid
+               CYAN,   // stateless
+       };
+
+       /* AltosMapInterface */
+       public void debug(String format, Object ... arguments) {
+               AltosDebug.debug(format, arguments);
+       }
+
+       class MapTile extends AltosMapTile {
+               public void paint(AltosMapTransform t) {
+                       AltosPointInt           pt = new AltosPointInt(t.screen(upper_left));
+
+                       if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
+                               return;
+
+                       AltosImage              altos_image = this.get_image();
+
+                       MapImage                map_image = (MapImage) altos_image;
+
+                       Bitmap                  bitmap = null;
+
+                       if (map_image != null)
+                               bitmap = map_image.bitmap;
+
+                       if (bitmap != null) {
+                               canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
+                       } else {
+                               paint.setColor(0xff808080);
+                               canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
+                               if (t.has_location()) {
+                                       String  message = null;
+                                       switch (status) {
+                                       case AltosMapTile.fetching:
+                                               message = "Fetching...";
+                                               break;
+                                       case AltosMapTile.bad_request:
+                                               message = "Internal error";
+                                               break;
+                                       case AltosMapTile.failed:
+                                               message = "Network error";
+                                               break;
+                                       case AltosMapTile.forbidden:
+                                               message = "Outside of known launch areas";
+                                               break;
+                                       }
+                                       if (message != null) {
+                                               Rect    bounds = new Rect();
+                                               paint.getTextBounds(message, 0, message.length(), bounds);
+
+                                               int     width = bounds.right - bounds.left;
+                                               int     height = bounds.bottom - bounds.top;
+
+                                               float x = pt.x + px_size / 2.0f;
+                                               float y = pt.y + px_size / 2.0f;
+                                               x = x - width / 2.0f;
+                                               y = y + height / 2.0f;
+                                               paint.setColor(0xff000000);
+                                               canvas.drawText(message, 0, message.length(), x, y, paint);
+                                       }
+                               }
+                       }
+               }
+
+               public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+                       super(cache, upper_left, center, zoom, maptype, px_size, scale);
+               }
+
+       }
+
+       public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+               return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
+       }
+
+       public AltosMapPath new_path() {
+               return null;
+       }
+
+       public AltosMapLine new_line() {
+               return null;
+       }
+
+       class MapImage implements AltosImage {
+               public Bitmap   bitmap;
+
+               public void flush() {
+                       if (bitmap != null) {
+                               bitmap.recycle();
+                               bitmap = null;
+                       }
+               }
+
+               public MapImage(File file) {
+                       bitmap = BitmapFactory.decodeFile(file.getPath());
+               }
+       }
+
+       public AltosImage load_image(File file) throws Exception {
+               return new MapImage(file);
+       }
+
+       class MapMark extends AltosMapMark {
+               public void paint(AltosMapTransform t) {
+               }
+
+               MapMark(double lat, double lon, int state) {
+                       super(lat, lon, state);
+               }
+       }
+
+       public AltosMapMark new_mark(double lat, double lon, int state) {
+               return new MapMark(lat, lon, state);
+       }
+
+       public int width() {
+               return getWidth();
+       }
+
+       public int height() {
+               return getHeight();
+       }
+
+       public void repaint() {
+               postInvalidate();
+       }
+
+       public void repaint(AltosRectangle damage) {
+               postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
+       }
+
+       public void set_zoom_label(String label) {
+       }
+
+       public void select_object(AltosLatLon latlon) {
+               if (map.transform == null)
+                       return;
+               ArrayList<Integer>      near = new ArrayList<Integer>();
+
+               for (Rocket rocket : sorted_rockets()) {
+                       if (rocket.position == null) {
+                               debug("rocket %d has no position\n", rocket.serial);
+                               continue;
+                       }
+                       double distance = map.transform.hypot(latlon, rocket.position);
+                       debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
+                       if (distance < rocket_bitmap.getWidth() * 2.0) {
+                               debug("selecting %d\n", rocket.serial);
+                               near.add(rocket.serial);
+                       }
+               }
+               if (near.size() != 0)
+                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
+       }
+
+       class Line {
+               AltosLatLon     a, b;
+
+               void paint() {
+                       if (a != null && b != null) {
+                               AltosPointDouble        a_screen = map.transform.screen(a);
+                               AltosPointDouble        b_screen = map.transform.screen(b);
+                               paint.setColor(0xff8080ff);
+                               canvas.drawLine((float) a_screen.x, (float) a_screen.y,
+                                                   (float) b_screen.x, (float) b_screen.y,
+                                                   paint);
+                       }
+               }
+
+               void set_a(AltosLatLon a) {
+                       this.a = a;
+               }
+
+               void set_b(AltosLatLon b) {
+                       this.b = b;
+               }
+
+               Line() {
+               }
+       }
+
+       Line line = new Line();
+
+       int     stroke_width = 20;
+
+       void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
+               if (lat_lon != null && map != null && map.transform != null) {
+                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+                       Rect    bounds = new Rect();
+                       paint.getTextBounds(text, 0, text.length(), bounds);
+
+                       int     width = bounds.right - bounds.left;
+                       int     height = bounds.bottom - bounds.top;
+
+                       float x = pt.x;
+                       float y = pt.y;
+                       x = x - width / 2.0f - off_x;
+                       y = y + height / 2.0f - off_y;
+                       paint.setColor(0xff000000);
+                       canvas.drawText(text, 0, text.length(), x, y, paint);
+               }
+       }
+
+       HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
+
+       void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
+               if (lat_lon != null && map != null && map.transform != null) {
+                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+                       canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
+               }
+       }
+
+       private Rocket[] sorted_rockets() {
+               Rocket[]        rocket_array = rockets.values().toArray(new Rocket[0]);
+
+               Arrays.sort(rocket_array);
+               return rocket_array;
+       }
+
+       private void draw_positions() {
+               line.set_a(there);
+               line.set_b(here);
+               line.paint();
+               draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
+
+               for (Rocket rocket : sorted_rockets())
+                       rocket.paint();
+               draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
+       }
+
+       @Override public void invalidate() {
+               Rect r = new Rect();
+               getDrawingRect(r);
+               super.invalidate();
+       }
+
+       @Override public void invalidate(int l, int t, int r, int b) {
+               Rect rect = new Rect();
+               getDrawingRect(rect);
+               super.invalidate();
+       }
+
+       @Override
+       protected void onDraw(Canvas view_canvas) {
+               if (map == null) {
+                       debug("MapView draw without map\n");
+                       return;
+               }
+               canvas = view_canvas;
+               paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+               paint.setStrokeWidth(stroke_width);
+               paint.setStrokeCap(Paint.Cap.ROUND);
+               paint.setStrokeJoin(Paint.Join.ROUND);
+               paint.setTextSize(40);
+               map.paint();
+               draw_positions();
+               canvas = null;
+       }
+
+       public boolean onScale(ScaleGestureDetector detector) {
+               float   f = detector.getScaleFactor();
+
+               if (f <= 0.8) {
+                       map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+                       return true;
+               }
+               if (f >= 1.2) {
+                       map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+                       return true;
+               }
+               return false;
+       }
+
+       public boolean onScaleBegin(ScaleGestureDetector detector) {
+               return true;
+       }
+
+       public void onScaleEnd(ScaleGestureDetector detector) {
+       }
+
+       @Override
+       public boolean dispatchTouchEvent(MotionEvent event) {
+               scale_detector.onTouchEvent(event);
+
+               if (scale_detector.isInProgress()) {
+                       scaling = true;
+               }
+
+               if (scaling) {
+                       if (event.getAction() == MotionEvent.ACTION_UP) {
+                               scaling = false;
+                       }
+                       return true;
+               }
+
+               if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                       map.touch_start((int) event.getX(), (int) event.getY(), true);
+               } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+                       map.touch_continue((int) event.getX(), (int) event.getY(), true);
+               } else if (event.getAction() == MotionEvent.ACTION_UP) {
+                       map.touch_stop((int) event.getX(), (int) event.getY(), true);
+               }
+               return true;
+       }
+
+       double  mapAccuracy;
+
+       public void center(double lat, double lon, double accuracy) {
+               if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
+                       if (map != null)
+                               map.maybe_centre(lat, lon);
+                       mapAccuracy = accuracy;
+               }
+       }
+
+       public void set_visible(boolean visible) {
+               if (visible)
+                       setVisibility(VISIBLE);
+               else
+                       setVisibility(GONE);
+       }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               boolean changed = false;
+
+               if (state != null) {
+                       map.show(state, null);
+                       if (state.pad_lat != AltosLib.MISSING && pad == null)
+                               pad = new AltosLatLon(state.pad_lat, state.pad_lon);
+               }
+
+               if (telem_state != null) {
+                       Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
+                       Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
+
+                       /* remove deleted keys */
+                       for (int serial : old_serial) {
+                               if (!telem_state.states.containsKey(serial))
+                                       rockets.remove(serial);
+                       }
+
+                       /* set remaining keys */
+
+                       for (int serial : new_serial) {
+                               Rocket          rocket;
+                               AltosState      t_state = telem_state.states.get(serial);
+                               if (rockets.containsKey(serial))
+                                       rocket = rockets.get(serial);
+                               else {
+                                       rocket = new Rocket(serial, this);
+                                       rockets.put(serial, rocket);
+                               }
+                               if (t_state.gps != null) {
+                                       AltosLatLon     latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
+                                       rocket.set_position(latlon, t_state.received_time);
+                                       if (state.cal_data().serial == serial)
+                                               there = latlon;
+                               }
+                               if (state != null)
+                                       rocket.set_active(state.cal_data().serial == serial);
+                       }
+               }
+               if (receiver != null) {
+                       AltosLatLon new_here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+                       if (!new_here.equals(here)) {
+                               here = new_here;
+                               AltosDebug.debug("Location changed, redraw");
+                               repaint();
+                       }
+               }
+       }
+
+       public void onCreateView(AltosDroid altos_droid) {
+               this.altos_droid = altos_droid;
+               map = new AltosMap(this, scale);
+               AltosPreferences.register_map_type_listener(this);
+               map.set_maptype(AltosPreferences.map_type());
+
+               pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
+               /* arrow at the bottom of the launchpad image */
+               pad_off_x = pad_bitmap.getWidth() / 2;
+               pad_off_y = pad_bitmap.getHeight();
+
+               rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
+               /* arrow at the bottom of the rocket image */
+               rocket_off_x = rocket_bitmap.getWidth() / 2;
+               rocket_off_y = rocket_bitmap.getHeight();
+
+               here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
+               /* Center of the dot */
+               here_off_x = here_bitmap.getWidth() / 2;
+               here_off_y = here_bitmap.getHeight() / 2;
+       }
+
+       public void onDestroyView() {
+               AltosPreferences.unregister_map_type_listener(this);
+       }
+
+       public void map_type_changed(int map_type) {
+               if (map != null)
+                       map.set_maptype(map_type);
+       }
+
+       public AltosMapOffline(Context context, AttributeSet attrs) {
+               super(context, attrs);
+               this.altos_droid = altos_droid;
+               scale_detector = new ScaleGestureDetector(context, this);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java
new file mode 100644 (file)
index 0000000..37e4435
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+import com.google.android.gms.maps.*;
+import com.google.android.gms.maps.model.*;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+//import android.support.v4.app.FragmentTransaction;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+import android.content.*;
+
+class RocketOnline implements Comparable {
+       Marker          marker;
+       int             serial;
+       long            last_packet;
+       int             size;
+
+       void set_position(AltosLatLon position, long last_packet) {
+               marker.setPosition(new LatLng(position.lat, position.lon));
+               this.last_packet = last_packet;
+       }
+
+       private Bitmap rocket_bitmap(Context context, String text) {
+
+               /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
+                */
+               Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
+               Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
+
+               Canvas canvas = new Canvas(bitmap);
+               Paint paint = new Paint();
+               paint.setTextSize(40);
+               paint.setColor(0xff000000);
+
+               Rect    bounds = new Rect();
+               paint.getTextBounds(text, 0, text.length(), bounds);
+
+               int     width = bounds.right - bounds.left;
+               int     height = bounds.bottom - bounds.top;
+
+               float x = bitmap.getWidth() / 2.0f - width / 2.0f;
+               float y = bitmap.getHeight() / 2.0f - height / 2.0f;
+
+               size = bitmap.getWidth();
+
+               canvas.drawText(text, 0, text.length(), x, y, paint);
+               return bitmap;
+       }
+
+       public void remove() {
+               marker.remove();
+       }
+
+       public int compareTo(Object o) {
+               RocketOnline other = (RocketOnline) o;
+
+               long    diff = last_packet - other.last_packet;
+
+               if (diff > 0)
+                       return 1;
+               if (diff < 0)
+                       return -1;
+               return 0;
+       }
+
+       RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
+               this.serial = serial;
+               String name = String.format("%d", serial);
+               this.marker = map.addMarker(new MarkerOptions()
+                                           .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
+                                           .position(new LatLng(lat, lon))
+                                           .visible(true));
+               this.last_packet = last_packet;
+       }
+}
+
+public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, AltosMapTypeListener {
+       public SupportMapFragment mMapFragment;
+       private GoogleMap mMap;
+       private boolean mapLoaded = false;
+       Context context;
+
+       private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
+       private Marker mPadMarker;
+       private boolean pad_set;
+       private Polyline mPolyline;
+
+       private View map_view;
+
+       private double mapAccuracy = -1;
+
+       private AltosLatLon my_position = null;
+       private AltosLatLon target_position = null;
+
+       private AltosDroid altos_droid;
+
+       public void onCreateView(AltosDroid altos_droid) {
+               this.altos_droid = altos_droid;
+               final int map_type = AltosPreferences.map_type();
+               AltosPreferences.register_map_type_listener(this);
+               mMapFragment = new SupportMapFragment() {
+                       @Override
+                       public void onActivityCreated(Bundle savedInstanceState) {
+                               super.onActivityCreated(savedInstanceState);
+                               setupMap(map_type);
+                       }
+                       @Override
+                       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+                               map_view = super.onCreateView(inflater, container, savedInstanceState);
+                               return map_view;
+                       }
+                       @Override
+                       public void onDestroyView() {
+                               super.onDestroyView();
+                               map_view = null;
+                       }
+               };
+       }
+
+       public void onDestroyView() {
+               AltosPreferences.unregister_map_type_listener(this);
+       }
+
+       private double pixel_distance(LatLng a, LatLng b) {
+               Projection projection = mMap.getProjection();
+
+               Point   a_pt = projection.toScreenLocation(a);
+               Point   b_pt = projection.toScreenLocation(b);
+
+               return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
+       }
+
+       private RocketOnline[] sorted_rockets() {
+               RocketOnline[]  rocket_array = rockets.values().toArray(new RocketOnline[0]);
+
+               Arrays.sort(rocket_array);
+               return rocket_array;
+       }
+
+       public void onMapClick(LatLng lat_lng) {
+               ArrayList<Integer>      near = new ArrayList<Integer>();
+
+               for (RocketOnline rocket : sorted_rockets()) {
+                       LatLng  pos = rocket.marker.getPosition();
+
+                       if (pos == null)
+                               continue;
+
+                       double distance = pixel_distance(lat_lng, pos);
+                       if (distance < rocket.size * 2)
+                               near.add(rocket.serial);
+               }
+
+               if (near.size() != 0)
+                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
+       }
+
+       public boolean onMarkerClick(Marker marker) {
+               onMapClick(marker.getPosition());
+               return true;
+       }
+
+       public void setupMap(int map_type) {
+               mMap = mMapFragment.getMap();
+               if (mMap != null) {
+                       map_type_changed(map_type);
+                       mMap.setMyLocationEnabled(true);
+                       mMap.getUiSettings().setTiltGesturesEnabled(false);
+                       mMap.getUiSettings().setZoomControlsEnabled(false);
+                       mMap.setOnMarkerClickListener(this);
+                       mMap.setOnMapClickListener(this);
+
+                       mPadMarker = mMap.addMarker(
+                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
+                                                          .position(new LatLng(0,0))
+                                                          .visible(false)
+                                       );
+
+                       mPolyline = mMap.addPolyline(
+                                       new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
+                                                            .width(20)
+                                                            .color(Color.BLUE)
+                                                            .visible(false)
+                                       );
+
+                       mapLoaded = true;
+               }
+       }
+
+       public void center(double lat, double lon, double accuracy) {
+               if (mMap == null)
+                       return;
+
+               if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
+                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
+                       mapAccuracy = accuracy;
+               }
+       }
+
+       private void set_rocket(int serial, AltosState state) {
+               RocketOnline    rocket;
+
+               if (state.gps == null || state.gps.lat == AltosLib.MISSING)
+                       return;
+
+               if (mMap == null)
+                       return;
+
+               if (rockets.containsKey(serial)) {
+                       rocket = rockets.get(serial);
+                       rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
+               } else {
+                       rocket = new RocketOnline(context,
+                                                 serial,
+                                                 mMap, state.gps.lat, state.gps.lon,
+                                                 state.received_time);
+                       rockets.put(serial, rocket);
+               }
+       }
+
+       private void remove_rocket(int serial) {
+               RocketOnline rocket = rockets.get(serial);
+               rocket.remove();
+               rockets.remove(serial);
+       }
+
+       public void set_visible(boolean visible) {
+               if (map_view == null)
+                       return;
+               if (visible)
+                       map_view.setVisibility(View.VISIBLE);
+               else
+                       map_view.setVisibility(View.GONE);
+       }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+
+               if (telem_state != null) {
+                       for (int serial : rockets.keySet()) {
+                               if (!telem_state.states.containsKey(serial))
+                                       remove_rocket(serial);
+                       }
+
+                       for (int serial : telem_state.states.keySet()) {
+                               set_rocket(serial, telem_state.states.get(serial));
+                       }
+               }
+
+               if (state != null) {
+                       if (mapLoaded) {
+                               if (!pad_set && state.pad_lat != AltosLib.MISSING) {
+                                       pad_set = true;
+                                       mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
+                                       mPadMarker.setVisible(true);
+                               }
+                       }
+                       if (state.gps != null && state.gps.lat != AltosLib.MISSING) {
+
+                               target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
+                               if (state.gps.locked && state.gps.nsat >= 4)
+                                       center (state.gps.lat, state.gps.lon, 10);
+                       }
+               }
+
+               if (receiver != null) {
+                       double accuracy;
+
+                       if (receiver.hasAccuracy())
+                               accuracy = receiver.getAccuracy();
+                       else
+                               accuracy = 1000;
+
+                       my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+                       center (my_position.lat, my_position.lon, accuracy);
+               }
+
+               if (my_position != null && target_position != null && mPolyline != null) {
+                       mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
+                       mPolyline.setVisible(true);
+               }
+
+       }
+
+       public void map_type_changed(int map_type) {
+               if (mMap != null) {
+                       if (map_type == AltosMap.maptype_hybrid)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
+                       else if (map_type == AltosMap.maptype_satellite)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
+                       else if (map_type == AltosMap.maptype_terrain)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
+                       else
+                               mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+               }
+       }
+
+       public AltosMapOnline(Context context) {
+               this.context = context;
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosUsb.java
new file mode 100644 (file)
index 0000000..0b235f2
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.hardware.usb.*;
+import android.app.*;
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosUsb extends AltosDroidLink {
+
+       private Thread           input_thread   = null;
+
+       private Handler          handler;
+
+       private UsbManager              manager;
+       private UsbDevice               device;
+       private UsbDeviceConnection     connection;
+       private UsbInterface            iface;
+       private UsbEndpoint             in, out;
+
+       private InputStream      input;
+       private OutputStream     output;
+
+       // Constructor
+       public AltosUsb(Context context, UsbDevice device, Handler handler) {
+               super(handler);
+//             set_debug(D);
+               this.handler = handler;
+
+               iface = null;
+               in = null;
+               out = null;
+
+               int     niface = device.getInterfaceCount();
+
+               for (int i = 0; i < niface; i++) {
+
+                       iface = device.getInterface(i);
+
+                       in = null;
+                       out = null;
+
+                       int nendpoints = iface.getEndpointCount();
+
+                       for (int e = 0; e < nendpoints; e++) {
+                               UsbEndpoint     endpoint = iface.getEndpoint(e);
+
+                               if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
+                                       switch (endpoint.getDirection()) {
+                                       case UsbConstants.USB_DIR_OUT:
+                                               out = endpoint;
+                                               break;
+                                       case UsbConstants.USB_DIR_IN:
+                                               in = endpoint;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (in != null && out != null)
+                               break;
+               }
+
+               if (in != null && out != null) {
+                       AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
+
+                       manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+                       if (manager == null) {
+                               AltosDebug.debug("USB_SERVICE failed");
+                               return;
+                       }
+
+                       connection = manager.openDevice(device);
+
+                       if (connection == null) {
+                               AltosDebug.debug("openDevice failed");
+                               return;
+                       }
+
+                       connection.claimInterface(iface, true);
+
+                       input_thread = new Thread(this);
+                       input_thread.start();
+
+                       // Configure the newly connected device for telemetry
+                       print("~\nE 0\n");
+                       set_monitor(false);
+               }
+       }
+
+       static private boolean isAltusMetrum(UsbDevice device) {
+               if (device.getVendorId() != AltosLib.vendor_altusmetrum)
+                       return false;
+               if (device.getProductId() < AltosLib.product_altusmetrum_min)
+                       return false;
+               if (device.getProductId() > AltosLib.product_altusmetrum_max)
+                       return false;
+               return true;
+       }
+
+       static boolean matchProduct(int want_product, UsbDevice device) {
+
+               if (!isAltusMetrum(device))
+                       return false;
+
+               if (want_product == AltosLib.product_any)
+                       return true;
+
+               int have_product = device.getProductId();
+
+               if (want_product == AltosLib.product_basestation)
+                       return have_product == AltosLib.product_teledongle ||
+                               have_product == AltosLib.product_teleterra ||
+                               have_product == AltosLib.product_telebt ||
+                               have_product == AltosLib.product_megadongle;
+
+               if (want_product == AltosLib.product_altimeter)
+                       return have_product == AltosLib.product_telemetrum ||
+                               have_product == AltosLib.product_telemega ||
+                               have_product == AltosLib.product_easymega ||
+                               have_product == AltosLib.product_telegps ||
+                               have_product == AltosLib.product_easymini ||
+                               have_product == AltosLib.product_telemini;
+
+               if (have_product == AltosLib.product_altusmetrum)       /* old devices match any request */
+                       return true;
+
+               if (want_product == have_product)
+                       return true;
+
+               return false;
+       }
+
+       static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
+               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+//             if (manager.hasPermission(device))
+//                     return true;
+
+               AltosDebug.debug("request permission for USB device " + device.toString());
+
+               manager.requestPermission(device, pi);
+               return false;
+       }
+
+       static public UsbDevice find_device(Context context, int match_product) {
+               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+               HashMap<String,UsbDevice>       devices = manager.getDeviceList();
+
+               for (UsbDevice  device : devices.values()) {
+                       int     vendor = device.getVendorId();
+                       int     product = device.getProductId();
+
+                       if (matchProduct(match_product, device)) {
+                               AltosDebug.debug("found USB device " + device.toString());
+                               return device;
+                       }
+               }
+
+               return null;
+       }
+
+       private void disconnected() {
+               if (closed()) {
+                       AltosDebug.debug("disconnected after closed");
+                       return;
+               }
+
+               AltosDebug.debug("Sending disconnected message");
+               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+       }
+
+       void close_device() {
+               UsbDeviceConnection     tmp_connection;
+
+               synchronized(this) {
+                       tmp_connection = connection;
+                       connection = null;
+               }
+
+               if (tmp_connection != null) {
+                       AltosDebug.debug("Closing USB device");
+                       tmp_connection.close();
+               }
+       }
+
+       int read(byte[] buffer, int len) {
+               int ret = connection.bulkTransfer(in, buffer, len, -1);
+               AltosDebug.debug("read(%d) = %d\n", len, ret);
+               return ret;
+       }
+
+       int write(byte[] buffer, int len) {
+               int ret = connection.bulkTransfer(out, buffer, len, -1);
+               AltosDebug.debug("write(%d) = %d\n", len, ret);
+               return ret;
+       }
+
+       // Stubs of required methods when extending AltosLink
+       public boolean can_cancel_reply()   { return false; }
+       public boolean show_reply_timeout() { return true; }
+       public void hide_reply_timeout()    { }
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosViewPager.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosViewPager.java
new file mode 100644 (file)
index 0000000..039ba14
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import android.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class AltosViewPager extends ViewPager {
+
+    public AltosViewPager(Context context) {
+        super(context);
+    }
+
+    public AltosViewPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+
+           if (v.getClass() != null &&
+               v.getClass().getName() != null &&
+               v.getClass().getName().endsWith("MapOffline"))
+                   return true;
+
+           if(v.getClass() != null &&
+              v.getClass().getPackage() != null &&
+              v.getClass().getPackage().getName() != null &&
+              v.getClass().getPackage().getName().startsWith("maps."))
+                   return true;
+
+           return super.canScroll(v, checkV, dx, x, y);
+    }
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosVoice.java
new file mode 100644 (file)
index 0000000..ae3299f
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.location.Location;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class AltosVoice {
+
+       private TextToSpeech tts         = null;
+       private boolean      tts_enabled = false;
+
+       static final int TELL_MODE_NONE = 0;
+       static final int TELL_MODE_PAD = 1;
+       static final int TELL_MODE_FLIGHT = 2;
+       static final int TELL_MODE_RECOVER = 3;
+
+       static final int TELL_FLIGHT_NONE = 0;
+       static final int TELL_FLIGHT_STATE = 1;
+       static final int TELL_FLIGHT_SPEED = 2;
+       static final int TELL_FLIGHT_HEIGHT = 3;
+       static final int TELL_FLIGHT_TRACK = 4;
+
+       private int             last_tell_mode;
+       private int             last_tell_serial = AltosLib.MISSING;
+       private int             last_state;
+       private AltosGPS        last_gps;
+       private double          last_height = AltosLib.MISSING;
+       private Location        last_receiver;
+       private long            last_speak_time;
+       private int             last_flight_tell = TELL_FLIGHT_NONE;
+       private boolean         quiet = false;
+
+       private long now() {
+               return System.currentTimeMillis();
+       }
+
+       private void reset_last() {
+               last_tell_mode = TELL_MODE_NONE;
+               last_speak_time = now() - 100 * 1000;
+               last_gps = null;
+               last_height = AltosLib.MISSING;
+               last_receiver = null;
+               last_state = AltosLib.ao_flight_invalid;
+               last_flight_tell = TELL_FLIGHT_NONE;
+       }
+
+       public AltosVoice(AltosDroid a) {
+               tts = new TextToSpeech(a, new OnInitListener() {
+                       public void onInit(int status) {
+                               if (status == TextToSpeech.SUCCESS) tts_enabled = true;
+                       }
+               });
+               reset_last();
+       }
+
+       public synchronized void set_enable(boolean enable) {
+               tts_enabled = enable;
+       }
+
+       public synchronized void speak(String s) {
+               if (!tts_enabled) return;
+               last_speak_time = now();
+               if (!quiet)
+                       tts.speak(s, TextToSpeech.QUEUE_ADD, null);
+       }
+
+       public synchronized long time_since_speak() {
+               return now() - last_speak_time;
+       }
+
+       public synchronized void speak(String format, Object ... arguments) {
+               speak(String.format(format, arguments));
+       }
+
+       public synchronized boolean is_speaking() {
+               return tts.isSpeaking();
+       }
+
+       public void stop() {
+               if (tts != null) {
+                       tts.stop();
+                       tts.shutdown();
+               }
+       }
+
+       private boolean         last_apogee_good;
+       private boolean         last_main_good;
+       private boolean         last_gps_good;
+
+       private boolean tell_gonogo(String name,
+                                 boolean current,
+                                 boolean previous,
+                                 boolean new_mode) {
+               if (current != previous || new_mode)
+                       speak("%s %s.", name, current ? "ready" : "not ready");
+               return current;
+       }
+
+       private boolean tell_pad(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver) {
+
+               if (state == null)
+                       return false;
+
+               AltosDebug.debug("tell_pad lag %b ltm %d\n", last_apogee_good, last_tell_mode);
+
+               if (state.apogee_voltage != AltosLib.MISSING)
+                       last_apogee_good = tell_gonogo("apogee",
+                                                      state.apogee_voltage >= AltosLib.ao_igniter_good,
+                                                      last_apogee_good,
+                                                      last_tell_mode != TELL_MODE_PAD);
+
+               if (state.main_voltage != AltosLib.MISSING)
+                       last_main_good = tell_gonogo("main",
+                                                    state.main_voltage >= AltosLib.ao_igniter_good,
+                                                    last_main_good,
+                                                    last_tell_mode != TELL_MODE_PAD);
+
+               if (state.gps != null)
+                       last_gps_good = tell_gonogo("G P S",
+                                                   state.gps_ready,
+                                                   last_gps_good,
+                                                   last_tell_mode != TELL_MODE_PAD);
+               return true;
+       }
+
+
+       private boolean descending(int state) {
+               return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
+       }
+
+       private boolean target_moved(AltosState state) {
+               if (last_gps != null && state != null && state.gps != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
+                                                                            state.gps.lat, state.gps.lon, state.gps.alt);
+                       double                  height_change = 0;
+                       double                  height = state.height();
+
+                       if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
+                               height_change = Math.abs(last_height - height);
+
+                       if (moved.range < 10 && height_change < 10)
+                               return false;
+               }
+               return true;
+       }
+
+       private boolean receiver_moved(Location receiver) {
+               if (last_receiver != null && receiver != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_receiver.getLatitude(),
+                                                                            last_receiver.getLongitude(),
+                                                                            last_receiver.getAltitude(),
+                                                                            receiver.getLatitude(),
+                                                                            receiver.getLongitude(),
+                                                                            receiver.getAltitude());
+                       if (moved.range < 10)
+                               return false;
+               }
+               return true;
+       }
+
+       private boolean tell_flight(TelemetryState telem_state, AltosState state,
+                                   AltosGreatCircle from_receiver, Location receiver) {
+
+               boolean spoken = false;
+
+               if (state == null)
+                       return false;
+
+               if (last_tell_mode != TELL_MODE_FLIGHT)
+                       last_flight_tell = TELL_FLIGHT_NONE;
+
+               if (state.state() != last_state && AltosLib.ao_flight_boost <= state.state() && state.state() <= AltosLib.ao_flight_landed) {
+                       speak(state.state_name());
+                       if (descending(state.state()) && !descending(last_state)) {
+                               if (state.max_height() != AltosLib.MISSING) {
+                                       speak("max height: %s.",
+                                             AltosConvert.height.say_units(state.max_height()));
+                               }
+                       }
+                       last_flight_tell = TELL_FLIGHT_STATE;
+                       return true;
+               }
+
+               if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
+                       if (time_since_speak() < 10 * 1000)
+                               return false;
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+               }
+
+               double  speed;
+               double  height;
+
+               if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
+                       last_flight_tell = TELL_FLIGHT_SPEED;
+
+                       if (state.state() <= AltosLib.ao_flight_coast) {
+                               speed = state.speed();
+                       } else {
+                               speed = state.gps_speed();
+                               if (speed == AltosLib.MISSING)
+                                       speed = state.speed();
+                       }
+
+                       if (speed != AltosLib.MISSING) {
+                               speak("speed: %s.", AltosConvert.speed.say_units(speed));
+                               return true;
+                       }
+               }
+
+               if (last_flight_tell == TELL_FLIGHT_SPEED) {
+                       last_flight_tell = TELL_FLIGHT_HEIGHT;
+                       height = state.height();
+
+                       if (height != AltosLib.MISSING) {
+                               speak("height: %s.", AltosConvert.height.say_units(height));
+                               return true;
+                       }
+               }
+
+               if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
+                       last_flight_tell = TELL_FLIGHT_TRACK;
+                       if (from_receiver != null) {
+                               speak("bearing %s %d, elevation %d, distance %s.",
+                                     from_receiver.bearing_words(
+                                             AltosGreatCircle.BEARING_VOICE),
+                                     (int) (from_receiver.bearing + 0.5),
+                                     (int) (from_receiver.elevation + 0.5),
+                                     AltosConvert.distance.say(from_receiver.distance));
+                               return true;
+                       }
+               }
+
+               return spoken;
+       }
+
+       private boolean tell_recover(TelemetryState telem_state, AltosState state,
+                                    AltosGreatCircle from_receiver, Location receiver) {
+
+               if (from_receiver == null)
+                       return false;
+
+               if (last_tell_mode == TELL_MODE_RECOVER) {
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+                       if (time_since_speak() <= 10 * 1000)
+                               return false;
+               }
+
+               String direction = AltosDroid.direction(from_receiver, receiver);
+               if (direction == null)
+                       direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
+
+               speak("%s, distance %s.", direction,
+                     AltosConvert.distance.say_units(from_receiver.distance));
+
+               return true;
+       }
+
+       public void tell(TelemetryState telem_state, AltosState state,
+                        AltosGreatCircle from_receiver, Location receiver,
+                        AltosDroidTab tab, boolean quiet) {
+
+               this.quiet = quiet;
+
+               boolean spoken = false;
+
+               if (!tts_enabled) return;
+
+               if (is_speaking()) return;
+
+               int     tell_serial = last_tell_serial;
+
+               if (state != null)
+                       tell_serial = state.cal_data().serial;
+
+               if (tell_serial != last_tell_serial)
+                       reset_last();
+
+               int     tell_mode = TELL_MODE_NONE;
+
+               if (tab.tab_name().equals(AltosDroid.tab_pad_name))
+                       tell_mode = TELL_MODE_PAD;
+               else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
+                       tell_mode = TELL_MODE_FLIGHT;
+               else
+                       tell_mode = TELL_MODE_RECOVER;
+
+               if (tell_mode == TELL_MODE_PAD)
+                       spoken = tell_pad(telem_state, state, from_receiver, receiver);
+               else if (tell_mode == TELL_MODE_FLIGHT)
+                       spoken = tell_flight(telem_state, state, from_receiver, receiver);
+               else
+                       spoken = tell_recover(telem_state, state, from_receiver, receiver);
+
+               if (spoken) {
+                       last_tell_mode = tell_mode;
+                       last_tell_serial = tell_serial;
+                       if (state != null) {
+                               last_state = state.state();
+                               last_height = state.height();
+                               if (state.gps != null)
+                                       last_gps = state.gps;
+                       }
+                       if (receiver != null)
+                               last_receiver = receiver;
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/BuildInfo.java.in b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/BuildInfo.java.in
new file mode 100644 (file)
index 0000000..aa6c9a7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+public class BuildInfo {
+       public static final String version      = "@VERSION@";
+       public static final String git_describe = "@DESCRIBE@";
+       public static final String branch       = "@BRANCH@";
+       public static final String commitnum    = "@COMMITNUM@";
+       public static final String commithash   = "@COMMITHASH@";
+       public static final String builddate    = "@BUILDDATE@";
+       public static final String buildtime    = "@BUILDTIME@";
+       public static final String buildtz      = "@BUILDTZ@";
+}
+
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceAddress.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceAddress.java
new file mode 100644 (file)
index 0000000..6f84556
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+public class DeviceAddress {
+       public String   address;
+       public String   name;
+
+       public DeviceAddress(String address, String name) {
+               this.address = address;
+               this.name = name;
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceListActivity.java
new file mode 100644 (file)
index 0000000..f36ef26
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.Set;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+/**
+ * This Activity appears as a dialog. It lists any paired devices and
+ * devices detected in the area after discovery. When a device is chosen
+ * by the user, the MAC address of the device is sent back to the parent
+ * Activity in the result Intent.
+ */
+public class DeviceListActivity extends Activity {
+
+       // Return Intent extra
+       public static final String EXTRA_DEVICE_ADDRESS = "device_address";
+       public static final String EXTRA_DEVICE_NAME = "device_name";
+
+       // Member fields
+       private BluetoothAdapter mBtAdapter;
+       private ArrayAdapter<String> mPairedDevicesArrayAdapter;
+       private ArrayAdapter<String> mNewDevicesArrayAdapter;
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.device_list);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+
+               // Initialize the button to perform device discovery
+               Button scanButton = (Button) findViewById(R.id.button_scan);
+               scanButton.setOnClickListener(new OnClickListener() {
+                       public void onClick(View v) {
+                               doDiscovery();
+                               v.setVisibility(View.GONE);
+                       }
+               });
+
+               // Initialize array adapters. One for already paired devices and
+               // one for newly discovered devices
+               mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
+               mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
+
+               // Find and set up the ListView for paired devices
+               ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
+               pairedListView.setAdapter(mPairedDevicesArrayAdapter);
+               pairedListView.setOnItemClickListener(mDeviceClickListener);
+
+               // Find and set up the ListView for newly discovered devices
+               ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
+               newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
+               newDevicesListView.setOnItemClickListener(mDeviceClickListener);
+
+               // Register for broadcasts when a device is discovered
+               IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+               this.registerReceiver(mReceiver, filter);
+
+               // Register for broadcasts when discovery has finished
+               filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+               this.registerReceiver(mReceiver, filter);
+
+               // Get the local Bluetooth adapter
+               mBtAdapter = BluetoothAdapter.getDefaultAdapter();
+
+               // Get a set of currently paired devices
+               Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
+
+               // If there are paired devices, add each one to the ArrayAdapter
+               if (pairedDevices.size() > 0) {
+                       findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
+                       for (BluetoothDevice device : pairedDevices)
+                               if (device.getName().startsWith("TeleBT"))
+                                       mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
+               } else {
+                       String noDevices = getResources().getText(R.string.none_paired).toString();
+                       mPairedDevicesArrayAdapter.add(noDevices);
+               }
+       }
+
+       @Override
+       protected void onDestroy() {
+               super.onDestroy();
+
+               // Make sure we're not doing discovery anymore
+               if (mBtAdapter != null) {
+                       mBtAdapter.cancelDiscovery();
+               }
+
+               // Unregister broadcast listeners
+               this.unregisterReceiver(mReceiver);
+       }
+
+       /**
+       * Start device discover with the BluetoothAdapter
+       */
+       private void doDiscovery() {
+               AltosDebug.debug("doDiscovery()");
+
+               // Indicate scanning in the title
+               setProgressBarIndeterminateVisibility(true);
+               setTitle(R.string.scanning);
+
+               // Turn on sub-title for new devices
+               findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
+
+               // If we're already discovering, stop it
+               if (mBtAdapter.isDiscovering()) {
+                       mBtAdapter.cancelDiscovery();
+               }
+
+               // Request discover from BluetoothAdapter
+               mBtAdapter.startDiscovery();
+       }
+
+       // The on-click listener for all devices in the ListViews
+       private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
+               public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
+                       // Cancel discovery because it's costly and we're about to connect
+                       mBtAdapter.cancelDiscovery();
+
+                       // Get the device MAC address, which is the last 17 chars in the View
+                       String info = ((TextView) v).getText().toString();
+                       String address = info.substring(info.length() - 17);
+
+                       int newline = info.indexOf('\n');
+
+                       String name = null;
+                       if (newline > 0)
+                               name = info.substring(0, newline);
+                       else
+                               name = info;
+
+                       AltosDebug.debug("******* selected item '%s'", info);
+
+                       // Create the result Intent and include the MAC address
+                       Intent intent = new Intent();
+                       intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
+                       intent.putExtra(EXTRA_DEVICE_NAME, name);
+
+                       // Set result and finish this Activity
+                       setResult(Activity.RESULT_OK, intent);
+                       finish();
+               }
+       };
+
+       // The BroadcastReceiver that listens for discovered devices and
+       // changes the title when discovery is finished
+       private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+               @Override
+               public void onReceive(Context context, Intent intent) {
+                       String action = intent.getAction();
+
+                       // When discovery finds a device
+                       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+
+                               /* Get the BluetoothDevice object from the Intent
+                                */
+                               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+                               /* If it's already paired, skip it, because it's been listed already
+                                */
+                               if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
+                               {
+                                       String  name = device.getName();
+                                       if (name != null && name.startsWith("TeleBT"))
+                                               mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+                               }
+
+                       /* When discovery is finished, change the Activity title
+                        */
+                       } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
+                               setProgressBarIndeterminateVisibility(false);
+                               setTitle(R.string.select_device);
+                               if (mNewDevicesArrayAdapter.getCount() == 0) {
+                                       String noDevices = getResources().getText(R.string.none_found).toString();
+                                       mNewDevicesArrayAdapter.add(noDevices);
+                               }
+                       }
+               }
+       };
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Dumper.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Dumper.java
new file mode 100644 (file)
index 0000000..2797fc5
--- /dev/null
@@ -0,0 +1,183 @@
+package org.altusmetrum.AltosDroid;
+
+       import java.lang.reflect.Array;
+       import java.lang.reflect.Field;
+       import java.util.HashMap;
+
+       public class Dumper {
+               private static Dumper instance = new Dumper();
+
+               protected static Dumper getInstance() {
+                       return instance;
+               }
+
+               class DumpContext {
+                       int maxDepth = 0;
+                       int maxArrayElements = 0;
+                       int callCount = 0;
+                       HashMap<String, String> ignoreList = new HashMap<String, String>();
+                       HashMap<Object, Integer> visited = new HashMap<Object, Integer>();
+               }
+
+               public static String dump(Object o) {
+                       return dump(o, 0, 0, null);
+               }
+
+               public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {
+                       DumpContext ctx = Dumper.getInstance().new DumpContext();
+                       ctx.maxDepth = maxDepth;
+                       ctx.maxArrayElements = maxArrayElements;
+
+                       if (ignoreList != null) {
+                               for (int i = 0; i < Array.getLength(ignoreList); i++) {
+                                       int colonIdx = ignoreList[i].indexOf(':');
+                                       if (colonIdx == -1)
+                                               ignoreList[i] = ignoreList[i] + ":";
+                                       ctx.ignoreList.put(ignoreList[i], ignoreList[i]);
+                               }
+                       }
+
+                       return dump(o, ctx);
+               }
+
+               protected static String dump(Object o, DumpContext ctx) {
+                       if (o == null) {
+                               return "<null>";
+                       }
+
+                       ctx.callCount++;
+                       StringBuffer tabs = new StringBuffer();
+                       for (int k = 0; k < ctx.callCount; k++) {
+                               tabs.append("\t");
+                       }
+                       StringBuffer buffer = new StringBuffer();
+                       @SuppressWarnings("rawtypes")
+                       Class oClass = o.getClass();
+
+                       String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);
+
+                       if (ctx.ignoreList.get(oSimpleName + ":") != null)
+                               return "<Ignored>";
+
+                       if (oClass.isArray()) {
+                               buffer.append("\n");
+                               buffer.append(tabs.toString().substring(1));
+                               buffer.append("[\n");
+                               int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));
+                               for (int i = 0; i < rowCount; i++) {
+                                       buffer.append(tabs.toString());
+                                       try {
+                                               Object value = Array.get(o, i);
+                                               buffer.append(dumpValue(value, ctx));
+                                       } catch (Exception e) {
+                                               buffer.append(e.getMessage());
+                                       }
+                                       if (i < Array.getLength(o) - 1)
+                                               buffer.append(",");
+                                       buffer.append("\n");
+                               }
+                               if (rowCount < Array.getLength(o)) {
+                                       buffer.append(tabs.toString());
+                                       buffer.append(Array.getLength(o) - rowCount + " more array elements...");
+                                       buffer.append("\n");
+                               }
+                               buffer.append(tabs.toString().substring(1));
+                               buffer.append("]");
+                       } else {
+                               buffer.append("\n");
+                               buffer.append(tabs.toString().substring(1));
+                               buffer.append("{\n");
+                               buffer.append(tabs.toString());
+                               buffer.append("hashCode: " + o.hashCode());
+                               buffer.append("\n");
+                               while (oClass != null && oClass != Object.class) {
+                                       Field[] fields = oClass.getDeclaredFields();
+
+                                       if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {
+                                               if (oClass != o.getClass()) {
+                                                       buffer.append(tabs.toString().substring(1));
+                                                       buffer.append("  Inherited from superclass " + oSimpleName + ":\n");
+                                               }
+
+                                               for (int i = 0; i < fields.length; i++) {
+
+                                                       String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());
+                                                       String fName = fields[i].getName();
+
+                                                       fields[i].setAccessible(true);
+                                                       buffer.append(tabs.toString());
+                                                       buffer.append(fName + "(" + fSimpleName + ")");
+                                                       buffer.append("=");
+
+                                                       if (ctx.ignoreList.get(":" + fName) == null &&
+                                                               ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&
+                                                               ctx.ignoreList.get(fSimpleName + ":") == null) {
+
+                                                               try {
+                                                                       Object value = fields[i].get(o);
+                                                                       buffer.append(dumpValue(value, ctx));
+                                                               } catch (Exception e) {
+                                                                       buffer.append(e.getMessage());
+                                                               }
+                                                               buffer.append("\n");
+                                                       } else {
+                                                               buffer.append("<Ignored>");
+                                                               buffer.append("\n");
+                                                       }
+                                               }
+                                               oClass = oClass.getSuperclass();
+                                               oSimpleName = oClass.getSimpleName();
+                                       } else {
+                                               oClass = null;
+                                               oSimpleName = "";
+                                       }
+                               }
+                               buffer.append(tabs.toString().substring(1));
+                               buffer.append("}");
+                       }
+                       ctx.callCount--;
+                       return buffer.toString();
+               }
+
+               protected static String dumpValue(Object value, DumpContext ctx) {
+                       if (value == null) {
+                               return "<null>";
+                       }
+                       if (value.getClass().isPrimitive() ||
+                               value.getClass() == java.lang.Short.class ||
+                               value.getClass() == java.lang.Long.class ||
+                               value.getClass() == java.lang.String.class ||
+                               value.getClass() == java.lang.Integer.class ||
+                               value.getClass() == java.lang.Float.class ||
+                               value.getClass() == java.lang.Byte.class ||
+                               value.getClass() == java.lang.Character.class ||
+                               value.getClass() == java.lang.Double.class ||
+                               value.getClass() == java.lang.Boolean.class) {
+
+                               return value.toString();
+
+                       } else {
+
+                               Integer visitedIndex = ctx.visited.get(value);
+                               if (visitedIndex == null) {
+                                       ctx.visited.put(value, ctx.callCount);
+                                       if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {
+                                               return dump(value, ctx);
+                                       } else {
+                                               return "<Reached max recursion depth>";
+                                       }
+                               } else {
+                                       return "<Previously visited - see hashCode " + value.hashCode() + ">";
+                               }
+                       }
+               }
+
+
+               private static String getSimpleNameWithoutArrayQualifier(@SuppressWarnings("rawtypes") Class clazz) {
+                       String simpleName = clazz.getSimpleName();
+                       int indexOfBracket = simpleName.indexOf('['); 
+                       if (indexOfBracket != -1)
+                               return simpleName.substring(0, indexOfBracket);
+                       return simpleName;
+               }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/GoNoGoLights.java
new file mode 100644 (file)
index 0000000..c18d730
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+import android.view.View;
+
+public class GoNoGoLights {
+       private Boolean state;
+       private Boolean missing;
+       private Boolean set;
+
+       private ImageView red;
+       private ImageView green;
+
+       private Drawable dRed;
+       private Drawable dGreen;
+       private Drawable dGray;
+
+       public GoNoGoLights(ImageView in_red, ImageView in_green, Resources r) {
+               red = in_red;
+               green = in_green;
+               state = false;
+               missing = true;
+               set = false;
+
+               dRed   = r.getDrawable(R.drawable.redled);
+               dGreen = r.getDrawable(R.drawable.greenled);
+               dGray  = r.getDrawable(R.drawable.grayled);
+       }
+
+       public void set(Boolean s, Boolean m) {
+               if (set && s == state && m == missing) return;
+               state = s;
+               missing = m;
+               set = true;
+               if (missing) {
+                       red.setImageDrawable(dGray);
+                       green.setImageDrawable(dGray);
+               } else if (state) {
+                       red.setImageDrawable(dGray);
+                       green.setImageDrawable(dGreen);
+               } else {
+                       red.setImageDrawable(dRed);
+                       green.setImageDrawable(dGray);
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java
new file mode 100644 (file)
index 0000000..3130d2a
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class IdleModeActivity extends Activity {
+       private EditText callsign;
+       private Button connect;
+       private Button disconnect;
+       private Button reboot;
+       private Button igniters;
+
+       public static final String EXTRA_IDLE_MODE = "idle_mode";
+       public static final String EXTRA_IDLE_RESULT = "idle_result";
+
+       public static final int IDLE_MODE_CONNECT = 1;
+       public static final int IDLE_MODE_REBOOT = 2;
+       public static final int IDLE_MODE_IGNITERS = 3;
+       public static final int IDLE_MODE_DISCONNECT = 4;
+
+       private void done(int type) {
+               AltosPreferences.set_callsign(callsign());
+               Intent intent = new Intent();
+               intent.putExtra(EXTRA_IDLE_RESULT, type);
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       private String callsign() {
+               return callsign.getEditableText().toString();
+       }
+
+       public void connect_idle() {
+               done(IDLE_MODE_CONNECT);
+       }
+
+       public void disconnect_idle() {
+               AltosDebug.debug("Disconnect idle button pressed");
+               done(IDLE_MODE_DISCONNECT);
+       }
+
+       public void reboot_idle() {
+               done(IDLE_MODE_REBOOT);
+       }
+
+       public void igniters_idle() {
+               done(IDLE_MODE_IGNITERS);
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.idle_mode);
+
+               callsign = (EditText) findViewById(R.id.set_callsign);
+               callsign.setText(new StringBuffer(AltosPreferences.callsign()));
+
+               connect = (Button) findViewById(R.id.connect_idle);
+               connect.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       connect_idle();
+                               }
+                       });
+               disconnect = (Button) findViewById(R.id.disconnect_idle);
+               disconnect.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       disconnect_idle();
+                               }
+                       });
+
+               boolean idle_mode = getIntent().getBooleanExtra(AltosDroid.EXTRA_IDLE_MODE, false);
+
+               if (idle_mode)
+                       connect.setVisibility(View.GONE);
+               else
+                       disconnect.setVisibility(View.GONE);
+
+               reboot = (Button) findViewById(R.id.reboot_idle);
+               reboot.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       reboot_idle();
+                               }
+                       });
+               igniters = (Button) findViewById(R.id.igniters_idle);
+               igniters.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       igniters_idle();
+                               }
+                       });
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IgniterActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IgniterActivity.java
new file mode 100644 (file)
index 0000000..a172451
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.*;
+import android.graphics.*;
+import android.os.*;
+import android.view.*;
+import android.view.View.*;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+class IgniterItem {
+       public String name;
+       public String pretty;
+       public String status;
+       public LinearLayout igniter_view = null;
+       public TextView pretty_view = null;
+       public TextView status_view = null;
+
+       private void update() {
+               if (pretty_view != null)
+                       pretty_view.setText(pretty);
+               if (status_view != null)
+                       status_view.setText(status);
+       }
+
+       public void set(String name, String pretty, String status) {
+               if (!name.equals(this.name) ||
+                   !pretty.equals(this.pretty) ||
+                   !status.equals(this.status))
+               {
+                       this.name = name;
+                       this.pretty = pretty;
+                       this.status = status;
+                       update();
+               }
+       }
+
+       public void realize(LinearLayout igniter_view,
+                           TextView pretty_view,
+                           TextView status_view) {
+               if (igniter_view != this.igniter_view ||
+                   pretty_view != this.pretty_view ||
+                   status_view != this.status_view)
+               {
+                       this.igniter_view = igniter_view;
+                       this.pretty_view = pretty_view;
+                       this.status_view = status_view;
+                       update();
+               }
+       }
+
+       public IgniterItem() {
+       }
+}
+
+class IgniterAdapter extends ArrayAdapter<IgniterItem> {
+       int resource;
+       int selected_item = -1;
+
+       public IgniterAdapter(Context context, int in_resource) {
+               super(context, in_resource);
+               resource = in_resource;
+       }
+
+       @Override
+       public View getView(int position, View convertView, ViewGroup parent) {
+               IgniterItem item = getItem(position);
+               if (item.igniter_view == null) {
+                       LinearLayout igniter_view = new LinearLayout(getContext());
+                       String inflater = Context.LAYOUT_INFLATER_SERVICE;
+                       LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
+                       li.inflate(resource, igniter_view, true);
+
+                       item.realize(igniter_view,
+                                    (TextView) igniter_view.findViewById(R.id.igniter_name),
+                                    (TextView) igniter_view.findViewById(R.id.igniter_status));
+               }
+               if (position == selected_item)
+                       item.igniter_view.setBackgroundColor(Color.RED);
+               else
+                       item.igniter_view.setBackgroundColor(Color.BLACK);
+               return item.igniter_view;
+       }
+}
+
+public class IgniterActivity extends Activity {
+       private ListView igniters_view;
+       private ToggleButton arm;
+       private Button fire;
+
+       private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();;
+
+       private IgniterAdapter igniters_adapter;
+
+       private boolean is_bound;
+       private Messenger service = null;
+       private final Messenger messenger = new Messenger(new IncomingHandler(this));
+
+       private Timer query_timer;
+       private boolean query_timer_running;
+
+       private Timer arm_timer;
+       private int arm_remaining;
+
+       public static final int IGNITER_QUERY = 1;
+       public static final int IGNITER_FIRE = 2;
+
+       // The Handler that gets information back from the Telemetry Service
+       static class IncomingHandler extends Handler {
+               private final WeakReference<IgniterActivity> igniter_activity;
+               IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); }
+
+               @Override
+               public void handleMessage(Message msg) {
+                       IgniterActivity ia = igniter_activity.get();
+
+                       switch (msg.what) {
+                       case AltosDroid.MSG_IGNITER_STATUS:
+                               ia.igniter_status((HashMap <String,Integer>) msg.obj);
+                               break;
+                       }
+               }
+       };
+
+
+       private ServiceConnection connection = new ServiceConnection() {
+               public void onServiceConnected(ComponentName className, IBinder binder) {
+                       service = new Messenger(binder);
+                       query_timer_tick();
+               }
+
+               public void onServiceDisconnected(ComponentName className) {
+                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
+                       service = null;
+               }
+       };
+
+       void doBindService() {
+               bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
+               is_bound = true;
+       }
+
+       void doUnbindService() {
+               if (is_bound) {
+                       // If we have received the service, and hence registered with it, then now is the time to unregister.
+                       unbindService(connection);
+                       is_bound = false;
+               }
+       }
+
+       private void done() {
+               Intent intent = new Intent();
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       class FireThread extends Thread {
+               private final String igniter;
+
+               @Override
+               public void run() {
+                       Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter);
+                       try {
+                               service.send(msg);
+                       } catch (RemoteException re) {
+                       }
+               }
+
+               public FireThread(String igniter) {
+                       this.igniter = igniter;
+               }
+       }
+
+       private void fire_igniter() {
+               if (igniters_adapter.selected_item >= 0) {
+                       IgniterItem     item = igniters_adapter.getItem(igniters_adapter.selected_item);
+                       FireThread      ft = new FireThread(item.name);
+                       ft.run();
+                       arm.setChecked(false);
+               }
+       }
+
+       private void arm_igniter(boolean is_checked) {
+               if (is_checked) {
+                       arm_timer_stop();
+                       arm_timer = new Timer();
+                       arm_remaining = 10;
+                       arm_set_text();
+                       fire.setEnabled(true);
+                       arm_timer.scheduleAtFixedRate(new TimerTask() {
+                                       public void run() {
+                                               arm_timer_tick();
+                                       }},
+                               1000L, 1000L);
+               } else {
+                       arm_timer_stop();
+                       fire.setEnabled(false);
+               }
+       }
+
+       private synchronized void query_timer_tick() {
+               if (query_timer_running)
+                       return;
+               if (service == null)
+                       return;
+               query_timer_running = true;
+               Thread thread = new Thread(new Runnable() {
+                               public void run() {
+                                       try {
+                                               Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY);
+                                               msg.replyTo = messenger;
+                                               if (service == null) {
+                                                       synchronized(IgniterActivity.this) {
+                                                               query_timer_running = false;
+                                                       }
+                                               } else
+                                                       service.send(msg);
+                                       } catch (RemoteException re) {
+                                               AltosDebug.debug("igniter query thread failed");
+                                               synchronized(IgniterActivity.this) {
+                                                       query_timer_running = false;
+                                               }
+                                       }
+                               }
+                       });
+               thread.start();
+       }
+
+       private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) {
+               if (!status.containsKey(name))
+                       return false;
+
+               IgniterItem item;
+               if (!igniters.containsKey(name)) {
+                       item = new IgniterItem();
+                       igniters.put(name, item);
+                       igniters_adapter.add(item);
+               } else
+                       item = igniters.get(name);
+
+               item.set(name, pretty, AltosIgnite.status_string(status.get(name)));
+               return true;
+       }
+
+       private synchronized void igniter_status(HashMap <String,Integer> status) {
+               query_timer_running = false;
+               if (status == null) {
+                       AltosDebug.debug("no igniter status");
+                       return;
+               }
+               set_igniter(status, "drogue", "Apogee");
+               set_igniter(status, "main", "Main");
+               for (int extra = 0;; extra++) {
+                       String  name = String.format("%d", extra);
+                       String  pretty = String.format("%c", 'A' + extra);
+                       if (!set_igniter(status, name, pretty))
+                               break;
+               }
+       }
+
+       private synchronized void arm_timer_stop() {
+               if (arm_timer != null) {
+                       arm_timer.cancel();
+                       arm_timer = null;
+               }
+               arm_remaining = 0;
+       }
+
+       private void arm_set_text() {
+               String  text = String.format("Armed %d", arm_remaining);
+
+               if (arm.isChecked())
+                       arm.setText(text);
+               arm.setTextOn(text);
+       }
+
+       private void arm_timer_tick() {
+               --arm_remaining;
+               if (arm_remaining <= 0) {
+                       arm_timer_stop();
+                       runOnUiThread(new Runnable() {
+                                       public void run() {
+                                               arm.setChecked(false);
+                                               fire.setEnabled(false);
+                                       }
+                               });
+               } else {
+                       runOnUiThread(new Runnable() {
+                                       public void run() {
+                                               arm_set_text();
+                                       }
+                               });
+               }
+       }
+
+       private void select_item(int position) {
+               if (position != igniters_adapter.selected_item) {
+                       if (igniters_adapter.selected_item >= 0)
+                               igniters_view.setItemChecked(igniters_adapter.selected_item, false);
+                       if (position >= 0) {
+                               igniters_view.setItemChecked(position, true);
+                               arm.setEnabled(true);
+                       } else
+                               arm.setEnabled(false);
+                       igniters_adapter.selected_item = position;
+               }
+       }
+
+       private class IgniterItemClickListener implements ListView.OnItemClickListener {
+               @Override
+               public void onItemClick(AdapterView<?> av, View v, int position, long id) {
+                       AltosDebug.debug("select %d\n", position);
+                       select_item(position);
+               }
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.igniters);
+
+               igniters_view = (ListView) findViewById(R.id.igniters);
+               igniters_view.setClickable(true);
+
+               igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status);
+
+               igniters_view.setAdapter(igniters_adapter);
+               igniters_view.setOnItemClickListener(new IgniterItemClickListener());
+
+               fire = (Button) findViewById(R.id.igniter_fire);
+               fire.setEnabled(false);
+               fire.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       fire_igniter();
+                               }
+                       });
+
+               arm = (ToggleButton) findViewById(R.id.igniter_arm);
+               arm.setEnabled(false);
+               arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
+                               public void onCheckedChanged(CompoundButton v, boolean is_checked) {
+                                       arm_igniter(is_checked);
+                               }
+                       });
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+
+       @Override
+       protected void onStart() {
+               super.onStart();
+               doBindService();
+       }
+
+       @Override
+       protected void onResume() {
+               super.onResume();
+               query_timer = new Timer(true);
+               query_timer.scheduleAtFixedRate(new TimerTask() {
+                               public void run() {
+                                       query_timer_tick();
+                               }},
+                       0L, 5000L);
+       }
+
+       @Override
+       protected void onPause() {
+               super.onPause();
+               if (query_timer != null) {
+                       query_timer.cancel();
+                       query_timer = null;
+               }
+               arm_timer_stop();
+               arm.setChecked(false);
+               fire.setEnabled(false);
+       }
+
+       @Override
+       protected void onStop() {
+               super.onStop();
+               doUnbindService();
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java
new file mode 100644 (file)
index 0000000..d510116
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+import java.text.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.content.*;
+import android.graphics.*;
+import android.os.*;
+import android.view.*;
+import android.view.View.*;
+import android.view.inputmethod.*;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+class FrequencyItem {
+       public AltosFrequency frequency;
+       public LinearLayout frequency_view = null;
+       public TextView pretty_view = null;
+
+       private void update() {
+               if (pretty_view != null && frequency != null)
+                       pretty_view.setText(frequency.toString());
+       }
+
+       public void realize(LinearLayout frequency_view,
+                           TextView pretty_view) {
+               if (frequency_view != this.frequency_view ||
+                   pretty_view != this.pretty_view)
+               {
+                       this.frequency_view = frequency_view;
+                       this.pretty_view = pretty_view;
+                       update();
+               }
+       }
+
+       public void set_frequency(AltosFrequency frequency) {
+               this.frequency = frequency;
+               update();
+       }
+
+       public FrequencyItem(AltosFrequency frequency) {
+               this.frequency = frequency;
+       }
+}
+
+class FrequencyAdapter extends ArrayAdapter<FrequencyItem> {
+       int resource;
+       int selected_item = -1;
+
+       public FrequencyAdapter(Context context, int in_resource) {
+               super(context, in_resource);
+               resource = in_resource;
+       }
+
+       public int count() {
+               int     count;
+
+               for (count = 0;; count++) {
+                       try {
+                               getItem(count);
+                       } catch (IndexOutOfBoundsException ie) {
+                               return count;
+                       }
+               }
+       }
+
+       @Override
+       public View getView(int position, View convertView, ViewGroup parent) {
+               FrequencyItem item = getItem(position);
+               if (item.frequency_view == null) {
+                       LinearLayout frequency_view = new LinearLayout(getContext());
+                       String inflater = Context.LAYOUT_INFLATER_SERVICE;
+                       LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
+                       li.inflate(resource, frequency_view, true);
+
+                       item.realize(frequency_view,
+                                    (TextView) frequency_view.findViewById(R.id.frequency));
+               }
+               if (position == selected_item)
+                       item.frequency_view.setBackgroundColor(Color.RED);
+               else
+                       item.frequency_view.setBackgroundColor(Color.BLACK);
+               return item.frequency_view;
+       }
+}
+
+public class ManageFrequenciesActivity extends Activity {
+       private ListView frequencies_view;
+
+       private Button set;
+       private Button remove;
+       private Button done;
+
+       private EditText set_frequency;
+       private EditText set_description;
+
+       private HashMap<String,FrequencyItem> frequencies = new HashMap<String,FrequencyItem>();;
+
+       private FrequencyAdapter frequencies_adapter;
+
+       private boolean is_bound;
+       private boolean changed = false;
+
+       private void done() {
+
+               set();
+
+               if (changed) {
+                       AltosFrequency[] frequencies = new AltosFrequency[frequencies_adapter.count()];
+                       for (int i = 0; i < frequencies.length; i++)
+                               frequencies[i] = frequencies_adapter.getItem(i).frequency;
+                       AltosPreferences.set_common_frequencies(frequencies);
+               }
+
+               Intent intent = new Intent();
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       private void load_item() {
+               if (frequencies_adapter.selected_item >= 0) {
+                       FrequencyItem item = frequencies_adapter.getItem(frequencies_adapter.selected_item);
+
+                       set_frequency.setText(item.frequency.frequency_string());
+                       set_description.setText(item.frequency.description);
+               } else {
+                       set_frequency.setText("");
+                       set_description.setText("");
+               }
+       }
+
+       private void select_item(int position) {
+               if (position != frequencies_adapter.selected_item) {
+                       if (frequencies_adapter.selected_item >= 0)
+                               frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
+                       if (position >= 0)
+                               frequencies_view.setItemChecked(position, true);
+                       frequencies_adapter.selected_item = position;
+               } else {
+                       if (frequencies_adapter.selected_item >= 0)
+                               frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
+                       frequencies_adapter.selected_item = -1;
+               }
+               load_item();
+       }
+
+       private int find(AltosFrequency frequency) {
+               for (int pos = 0; pos < frequencies_adapter.getCount(); pos++) {
+                       FrequencyItem   item = frequencies_adapter.getItem(pos);
+                       if (item.frequency.frequency == frequency.frequency &&
+                           item.frequency.description.equals(frequency.description))
+                               return pos;
+               }
+               return -1;
+       }
+
+       private int insert_item(AltosFrequency frequency) {
+               FrequencyItem new_item = new FrequencyItem(frequency);
+               int     pos;
+               for (pos = 0; pos < frequencies_adapter.getCount(); pos++) {
+                       FrequencyItem   item = frequencies_adapter.getItem(pos);
+                       if (item.frequency.frequency == new_item.frequency.frequency) {
+                               item.set_frequency(frequency);
+                               return pos;
+                       }
+                       if (item.frequency.frequency > new_item.frequency.frequency)
+                               break;
+               }
+               frequencies_adapter.insert(new_item, pos);
+               return pos;
+       }
+
+       private class FrequencyItemClickListener implements ListView.OnItemClickListener {
+               @Override
+               public void onItemClick(AdapterView<?> av, View v, int position, long id) {
+                       select_item(position);
+               }
+       }
+
+       private void hide_keyboard() {
+               InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
+               View view = getCurrentFocus();
+               if (view != null)
+                       imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+       }
+
+       private void set() {
+               String  frequency_text = set_frequency.getEditableText().toString();
+               String  description_text = set_description.getEditableText().toString();
+
+               try {
+                       double  f = AltosParse.parse_double_locale(frequency_text);
+                       AltosFrequency frequency = new AltosFrequency(f, description_text);
+                       int pos;
+
+                       pos = find(frequency);
+                       if (pos < 0) {
+                               pos = insert_item(frequency);
+                               changed = true;
+                       }
+                       frequencies_adapter.selected_item = -1;
+                       select_item(pos);
+               } catch (ParseException pe) {
+               }
+               hide_keyboard();
+       }
+
+       private void remove() {
+               if (frequencies_adapter.selected_item >= 0) {
+                       frequencies_adapter.remove(frequencies_adapter.getItem(frequencies_adapter.selected_item));
+                       select_item(-1);
+                       frequencies_view.setAdapter(frequencies_adapter);
+                       changed = true;
+               }
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.manage_frequencies);
+
+               frequencies_view = (ListView) findViewById(R.id.frequencies);
+               frequencies_view.setClickable(true);
+
+               frequencies_adapter = new FrequencyAdapter(this, R.layout.frequency);
+
+               frequencies_view.setAdapter(frequencies_adapter);
+               frequencies_view.setOnItemClickListener(new FrequencyItemClickListener());
+
+               AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
+               for (AltosFrequency frequency : frequencies)
+                       insert_item(frequency);
+
+               set_frequency = (EditText) findViewById(R.id.set_frequency);
+               set_description = (EditText) findViewById(R.id.set_description);
+
+               set = (Button) findViewById(R.id.set);
+               set.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       set();
+                               }
+                       });
+
+               remove = (Button) findViewById(R.id.remove);
+               remove.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       remove();
+                               }
+                       });
+
+               done = (Button) findViewById(R.id.done);
+               done.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       done();
+                               }
+                       });
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+
+       @Override
+       protected void onStart() {
+               super.onStart();
+       }
+
+       @Override
+       protected void onResume() {
+               super.onResume();
+       }
+
+       @Override
+       protected void onPause() {
+               super.onPause();
+       }
+
+       @Override
+       protected void onStop() {
+               super.onStop();
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/MapTypeActivity.java
new file mode 100644 (file)
index 0000000..ec88d2d
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class MapTypeActivity extends Activity {
+       private Button hybrid;
+       private Button satellite;
+       private Button roadmap;
+       private Button terrain;
+       private int selected_type;
+
+       public static final String EXTRA_MAP_TYPE = "map_type";
+
+       private void done(int type) {
+
+               Intent intent = new Intent();
+               intent.putExtra(EXTRA_MAP_TYPE, type);
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       public void selectType(View view) {
+               AltosDebug.debug("selectType %s", view.toString());
+               if (view == hybrid)
+                       done(AltosMap.maptype_hybrid);
+               if (view == satellite)
+                       done(AltosMap.maptype_satellite);
+               if (view == roadmap)
+                       done(AltosMap.maptype_roadmap);
+               if (view == terrain)
+                       done(AltosMap.maptype_terrain);
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.map_type);
+
+               hybrid = (Button) findViewById(R.id.map_type_hybrid);
+               satellite = (Button) findViewById(R.id.map_type_satellite);
+               roadmap = (Button) findViewById(R.id.map_type_roadmap);
+               terrain = (Button) findViewById(R.id.map_type_terrain);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/PreloadMapActivity.java
new file mode 100644 (file)
index 0000000..e393b56
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+import java.text.*;
+
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.location.Criteria;
+
+import org.altusmetrum.altoslib_13.*;
+
+/**
+ * This Activity appears as a dialog. It lists any paired devices and
+ * devices detected in the area after discovery. When a device is chosen
+ * by the user, the MAC address of the device is sent back to the parent
+ * Activity in the result Intent.
+ */
+public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapLoaderListener, LocationListener {
+
+       private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
+
+/*
+       private CheckBox        hybrid;
+       private CheckBox        satellite;
+       private CheckBox        roadmap;
+       private CheckBox        terrain;
+*/
+
+       private Spinner         known_sites_spinner;
+       private Spinner         min_zoom;
+       private Spinner         max_zoom;
+       private TextView        radius_label;
+       private Spinner         radius;
+
+       private EditText        latitude;
+       private EditText        longitude;
+
+       private ProgressBar     progress;
+
+       private AltosMapLoader  loader;
+
+       long    loader_notify_time;
+
+       /* AltosMapLoaderListener interfaces */
+       public void loader_start(final int max) {
+               loader_notify_time = System.currentTimeMillis();
+
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setMax(max);
+                                       progress.setProgress(0);
+                               }
+                       });
+       }
+
+       public void loader_notify(final int cur, final int max, final String name) {
+               long    now = System.currentTimeMillis();
+
+               if (now - loader_notify_time < 100)
+                       return;
+
+               loader_notify_time = now;
+
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setProgress(cur);
+                               }
+                       });
+       }
+
+       public void loader_done(int max) {
+               loader = null;
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setProgress(0);
+                                       finish();
+                               }
+                       });
+       }
+
+       public void debug(String format, Object ... arguments) {
+               AltosDebug.debug(format, arguments);
+       }
+
+       /* AltosLaunchSiteListener interface */
+
+       public void notify_launch_sites(final List<AltosLaunchSite> sites) {
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       for (AltosLaunchSite site : sites)
+                                               known_sites_adapter.add(site);
+                               }
+                       });
+       }
+
+       /* LocationProvider interface */
+
+       AltosLaunchSite current_location_site;
+
+       public void onLocationChanged(Location location) {
+               AltosDebug.debug("location changed");
+               if (current_location_site == null) {
+                       AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
+
+                       current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
+                       known_sites_adapter.insert(current_location_site, 0);
+
+                       if (selected_item != null)
+                               known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
+                       else {
+                               latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
+                               longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
+                       }
+               } else {
+                       current_location_site.latitude = location.getLatitude();
+                       current_location_site.longitude = location.getLongitude();
+               }
+       }
+
+       public void onStatusChanged(String provider, int status, Bundle extras) {
+       }
+
+       public void onProviderEnabled(String provider) {
+       }
+
+       public void onProviderDisabled(String provider) {
+       }
+
+       private double text(EditText view) throws ParseException {
+               return AltosParse.parse_double_locale(view.getEditableText().toString());
+       }
+
+       private double latitude() throws ParseException {
+               return text(latitude);
+       }
+
+       private double longitude() throws ParseException {
+               return text(longitude);
+       }
+
+       private int value(Spinner spinner) {
+               return (Integer) spinner.getSelectedItem();
+       }
+
+       private int min_z() {
+               return value(min_zoom);
+       }
+
+       private int max_z() {
+               return value(max_zoom);
+       }
+
+       private double value_distance(Spinner spinner) {
+               return (Double) spinner.getSelectedItem();
+       }
+
+       private double radius() {
+               double r = value_distance(radius);
+               if (AltosPreferences.imperial_units())
+                       r = AltosConvert.miles_to_meters(r);
+               else
+                       r = r * 1000;
+               return r;
+       }
+
+/*
+       private int bit(CheckBox box, int value) {
+               if (box.isChecked())
+                       return 1 << value;
+               return 0;
+       }
+*/
+
+       private int types() {
+/*
+               return (bit(hybrid, AltosMap.maptype_hybrid) |
+                       bit(satellite, AltosMap.maptype_satellite) |
+                       bit(roadmap, AltosMap.maptype_roadmap) |
+                       bit(terrain, AltosMap.maptype_terrain));
+*/
+               return 1 << AltosMap.maptype_hybrid;
+       }
+
+       private void load() {
+               if (loader != null)
+                       return;
+
+               try {
+                       double  lat = latitude();
+                       double  lon = longitude();
+                       int     min = min_z();
+                       int     max = max_z();
+                       double  r = radius();
+                       int     t = types();
+
+                       AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
+                                        lat, lon, min, max, r, t);
+                       loader = new AltosMapLoader(this, lat, lon, min, max, r, t, AltosMapOffline.scale);
+               } catch (ParseException e) {
+                       AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
+               }
+       }
+
+       private void add_numbers(Spinner spinner, int min, int max, int def) {
+
+               ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
+
+               int     spinner_def = 0;
+               int     pos = 0;
+
+               for (int i = min; i <= max; i++) {
+                       adapter.add(new Integer(i));
+                       if (i == def)
+                               spinner_def = pos;
+                       pos++;
+               }
+
+               spinner.setAdapter(adapter);
+               spinner.setSelection(spinner_def);
+       }
+
+
+       private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
+
+               ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
+
+               int     spinner_def = 0;
+               int     pos = 0;
+
+               double[] distances;
+               double  def;
+               if (AltosPreferences.imperial_units()) {
+                       distances = distances_mi;
+                       def = def_mi;
+               } else {
+                       distances = distances_km;
+                       def = def_km;
+               }
+
+               for (int i = 0; i < distances.length; i++) {
+                       adapter.add(distances[i]);
+                       if (distances[i] == def)
+                               spinner_def = pos;
+                       pos++;
+               }
+
+               spinner.setAdapter(adapter);
+               spinner.setSelection(spinner_def);
+       }
+
+
+
+       class SiteListListener implements OnItemSelectedListener {
+               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                       AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
+                       latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
+                       longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
+               }
+               public void onNothingSelected(AdapterView<?> parent) {
+               }
+
+               public SiteListListener() {
+               }
+       }
+
+       double[]        radius_mi = { 1, 2, 5, 10, 20 };
+       double          radius_def_mi = 2;
+       double[]        radius_km = { 1, 2, 5, 10, 20, 30 };
+       double          radius_def_km = 2;
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.map_preload);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+
+               // Initialize the button to perform device discovery
+               Button loadButton = (Button) findViewById(R.id.preload_load);
+               loadButton.setOnClickListener(new OnClickListener() {
+                       public void onClick(View v) {
+                               load();
+                       }
+               });
+
+               latitude = (EditText) findViewById(R.id.preload_latitude);
+               longitude = (EditText) findViewById(R.id.preload_longitude);
+
+/*
+               hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
+               satellite = (CheckBox) findViewById(R.id.preload_satellite);
+               roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
+               terrain = (CheckBox) findViewById(R.id.preload_terrain);
+
+               hybrid.setChecked(true);
+*/
+
+               min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
+               add_numbers(min_zoom,
+                           AltosMap.min_zoom - AltosMap.default_zoom,
+                           AltosMap.max_zoom - AltosMap.default_zoom, -2);
+               max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
+               add_numbers(max_zoom,
+                           AltosMap.min_zoom - AltosMap.default_zoom,
+                           AltosMap.max_zoom - AltosMap.default_zoom, 2);
+               radius_label = (TextView) findViewById(R.id.preload_radius_label);
+               radius = (Spinner) findViewById(R.id.preload_radius);
+               if (AltosPreferences.imperial_units())
+                       radius_label.setText("Radius (miles)");
+               else
+                       radius_label.setText("Radius (km)");
+               add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
+
+               progress = (ProgressBar) findViewById(R.id.preload_progress);
+
+               // Initialize array adapters. One for already paired devices and
+               // one for newly discovered devices
+               known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
+
+               known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
+
+               known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+               known_sites_spinner.setAdapter(known_sites_adapter);
+               known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
+
+               // Listen for GPS and Network position updates
+               LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+               locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
+
+               new AltosLaunchSites(this);
+       }
+
+       @Override
+       protected void onDestroy() {
+               super.onDestroy();
+
+               if (loader != null)
+                       loader.abort();
+
+               // Stop listening for location updates
+               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SetupActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SetupActivity.java
new file mode 100644 (file)
index 0000000..bad0b82
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.*;
+import android.content.*;
+import android.os.*;
+import android.view.*;
+import android.view.View.*;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class SetupActivity extends Activity {
+       private Spinner select_rate;
+       private Spinner set_units;
+       private Spinner map_type;
+       private Spinner map_source;
+       private Button manage_frequencies;
+       private Button preload_maps;
+       private Button done;
+
+       private boolean is_bound;
+       private Messenger service = null;
+
+       public final static String EXTRA_SETUP_CHANGES = "setup_changes";
+
+       private ServiceConnection connection = new ServiceConnection() {
+               public void onServiceConnected(ComponentName className, IBinder binder) {
+                       service = new Messenger(binder);
+               }
+
+               public void onServiceDisconnected(ComponentName className) {
+                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
+                       service = null;
+               }
+       };
+
+       void doBindService() {
+               bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
+               is_bound = true;
+       }
+
+       void doUnbindService() {
+               if (is_bound) {
+                       // If we have received the service, and hence registered with it, then now is the time to unregister.
+                       unbindService(connection);
+                       is_bound = false;
+               }
+       }
+
+       static final String[] rates = {
+               "38400",
+               "9600",
+               "2400",
+       };
+
+       static final String[] map_types = {
+               "Hybrid",
+               "Satellite",
+               "Roadmap",
+               "Terrain"
+       };
+
+       static final int[] map_type_values = {
+               AltosMap.maptype_hybrid,
+               AltosMap.maptype_satellite,
+               AltosMap.maptype_roadmap,
+               AltosMap.maptype_terrain,
+       };
+
+       static final String[] map_sources = {
+               "Online",
+               "Offline"
+       };
+
+       private int     set_telemetry_rate;
+       private int     set_map_source;
+       private int     set_map_type;
+       private boolean set_imperial_units;
+
+       private int     changes = 0;
+
+       private void add_change(int change) {
+               changes |= change;
+       }
+
+       private void done() {
+               Intent intent = new Intent();
+               if ((changes & AltosDroid.SETUP_BAUD) != 0)
+                       AltosPreferences.set_telemetry_rate(1, set_telemetry_rate);
+               if ((changes & AltosDroid.SETUP_UNITS) != 0)
+                       AltosPreferences.set_imperial_units(set_imperial_units);
+               if ((changes & AltosDroid.SETUP_MAP_SOURCE) != 0)
+                       AltosDroidPreferences.set_map_source(set_map_source);
+               if ((changes & AltosDroid.SETUP_MAP_TYPE) != 0)
+                       AltosPreferences.set_map_type(set_map_type);
+               intent.putExtra(EXTRA_SETUP_CHANGES, changes);
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       private void add_strings(Spinner spinner, String[] strings, int def) {
+               ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
+
+               for (int i = 0; i < strings.length; i++)
+                       adapter.add(strings[i]);
+
+               spinner.setAdapter(adapter);
+               if (def >= 0)
+                       spinner.setSelection(def);
+       }
+
+       private int default_rate_pos() {
+               int     default_rate = AltosPreferences.telemetry_rate(1);
+
+               for (int pos = 0; pos < rates.length; pos++) {
+                       if (string_to_rate(rates[pos]) == default_rate)
+                               return pos;
+               }
+               return -1;
+       }
+
+       private void setBaud(int baud) {
+               try {
+                       service.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
+                       set_telemetry_rate = baud;
+                       add_change(AltosDroid.SETUP_BAUD);
+               } catch (RemoteException e) {
+               }
+       }
+
+       private int string_to_rate(String baud) {
+               int     rate = AltosLib.ao_telemetry_rate_38400;
+               try {
+                       int     value = Integer.parseInt(baud);
+                       switch (value) {
+                       case 2400:
+                               rate = AltosLib.ao_telemetry_rate_2400;
+                               break;
+                       case 9600:
+                               rate = AltosLib.ao_telemetry_rate_9600;
+                               break;
+                       case 38400:
+                               rate = AltosLib.ao_telemetry_rate_38400;
+                               break;
+                       }
+               } catch (NumberFormatException e) {
+               }
+               return rate;
+       }
+
+       private void setBaud(String baud) {
+               setBaud(string_to_rate(baud));
+       }
+
+       private void select_rate(int pos) {
+               setBaud(rates[pos]);
+       }
+
+       static final String[] units = {
+               "Metric",
+               "Imperial"
+       };
+
+       private int default_units_pos() {
+               boolean imperial = AltosPreferences.imperial_units();
+
+               if (imperial)
+                       return 1;
+               return 0;
+       }
+
+       private void set_units(int pos) {
+               switch (pos) {
+               default:
+                       set_imperial_units = false;
+                       break;
+               case 1:
+                       set_imperial_units = true;
+                       break;
+               }
+               add_change(AltosDroid.SETUP_UNITS);
+       }
+
+       private int default_map_type_pos() {
+               int     default_map_type = AltosPreferences.map_type();
+
+               for (int pos = 0; pos < map_types.length; pos++)
+                       if (map_type_values[pos] == default_map_type)
+                               return pos;
+               return 0;
+       }
+
+       private void select_map_type(int pos) {
+               set_map_type = map_type_values[pos];
+               add_change(AltosDroid.SETUP_MAP_TYPE);
+       }
+
+       private int default_map_source_pos() {
+               int     default_source = AltosDroidPreferences.map_source();
+
+               switch (default_source) {
+               case AltosDroidPreferences.MAP_SOURCE_OFFLINE:
+                       return 1;
+               default:
+                       return 0;
+               }
+       }
+
+       private void select_map_source(int pos) {
+               switch (pos) {
+               default:
+                       set_map_source = AltosDroidPreferences.MAP_SOURCE_ONLINE;
+                       break;
+               case 1:
+                       set_map_source = AltosDroidPreferences.MAP_SOURCE_OFFLINE;
+                       break;
+               }
+               add_change(AltosDroid.SETUP_MAP_SOURCE);
+       }
+
+       private void manage_frequencies(){
+               Intent intent = new Intent(this, ManageFrequenciesActivity.class);
+               startActivity(intent);
+       }
+
+       private void preload_maps(){
+               Intent intent = new Intent(this, PreloadMapActivity.class);
+               startActivity(intent);
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               AltosDebug.init(this);
+               AltosDebug.debug("+++ ON CREATE +++");
+
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.setup);
+
+               select_rate = (Spinner) findViewById(R.id.select_rate);
+               add_strings(select_rate, rates, default_rate_pos());
+               select_rate.setOnItemSelectedListener(new OnItemSelectedListener() {
+                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                                       select_rate(pos);
+                               }
+                               public void onNothingSelected(AdapterView<?> parent) {
+                               }
+                       });
+
+               set_units = (Spinner) findViewById(R.id.set_units);
+               add_strings(set_units, units, default_units_pos());
+               set_units.setOnItemSelectedListener(new OnItemSelectedListener() {
+                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                                       set_units(pos);
+                               }
+                               public void onNothingSelected(AdapterView<?> parent) {
+                               }
+                       });
+
+               map_type = (Spinner) findViewById(R.id.map_type);
+               add_strings(map_type, map_types, default_map_type_pos());
+               map_type.setOnItemSelectedListener(new OnItemSelectedListener() {
+                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                                       select_map_type(pos);
+                               }
+                               public void onNothingSelected(AdapterView<?> parent) {
+                               }
+                       });
+
+               map_source = (Spinner) findViewById(R.id.map_source);
+               add_strings(map_source, map_sources, default_map_source_pos());
+               map_source.setOnItemSelectedListener(new OnItemSelectedListener() {
+                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                                       select_map_source(pos);
+                               }
+                               public void onNothingSelected(AdapterView<?> parent) {
+                               }
+                       });
+
+
+               manage_frequencies = (Button) findViewById(R.id.manage_frequencies);
+               manage_frequencies.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       manage_frequencies();
+                               }
+                       });
+
+               preload_maps = (Button) findViewById(R.id.preload_maps);
+               preload_maps.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       preload_maps();
+                               }
+                       });
+
+               done = (Button) findViewById(R.id.done);
+               done.setOnClickListener(new OnClickListener() {
+                               public void onClick(View v) {
+                                       done();
+                               }
+                       });
+
+               // Set result for when the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+
+       @Override
+       protected void onStart() {
+               super.onStart();
+               doBindService();
+       }
+
+       @Override
+       protected void onStop() {
+               super.onStop();
+               doUnbindService();
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabFlight.java
new file mode 100644 (file)
index 0000000..8997d96
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+
+public class TabFlight extends AltosDroidTab {
+       private TextView speed_view;
+       private TextView height_view;
+       private TextView max_speed_view;
+       private TextView max_height_view;
+       private TextView elevation_view;
+       private TextView range_view;
+       private TextView bearing_view;
+       private TextView compass_view;
+       private TextView distance_view;
+       private TextView latitude_view;
+       private TextView longitude_view;
+       private View apogee_view;
+       private TextView apogee_voltage_view;
+       private TextView apogee_voltage_label;
+       private GoNoGoLights apogee_lights;
+       private View main_view;
+       private TextView main_voltage_view;
+       private TextView main_voltage_label;
+       private GoNoGoLights main_lights;
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               View v = inflater.inflate(R.layout.tab_flight, container, false);
+
+               speed_view     = (TextView) v.findViewById(R.id.speed_value);
+               height_view    = (TextView) v.findViewById(R.id.height_value);
+               max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
+               max_height_view= (TextView) v.findViewById(R.id.max_height_value);
+               elevation_view = (TextView) v.findViewById(R.id.elevation_value);
+               range_view     = (TextView) v.findViewById(R.id.range_value);
+               bearing_view   = (TextView) v.findViewById(R.id.bearing_value);
+               compass_view   = (TextView) v.findViewById(R.id.compass_value);
+               distance_view  = (TextView) v.findViewById(R.id.distance_value);
+               latitude_view  = (TextView) v.findViewById(R.id.lat_value);
+               longitude_view = (TextView) v.findViewById(R.id.lon_value);
+
+               apogee_view = v.findViewById(R.id.apogee_view);
+               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+                                                (ImageView) v.findViewById(R.id.apogee_greenled),
+                                                getResources());
+               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+
+               main_view = v.findViewById(R.id.main_view);
+               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+                                              (ImageView) v.findViewById(R.id.main_greenled),
+                                              getResources());
+               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+
+               return v;
+       }
+
+       public String tab_name() { return AltosDroid.tab_flight_name; }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (state != null) {
+                       set_value(speed_view, AltosConvert.speed, 6, state.speed());
+                       set_value(height_view, AltosConvert.height, 6, state.height());
+                       set_value(max_speed_view, AltosConvert.speed, 6, state.max_speed());
+                       set_value(max_height_view, AltosConvert.height, 6, state.max_height());
+                       if (from_receiver != null) {
+                               elevation_view.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
+                               set_value(range_view, AltosConvert.distance, 6, from_receiver.range);
+                               bearing_view.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
+                               compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
+                               set_value(distance_view, AltosConvert.distance, 6, from_receiver.distance);
+                       } else { 
+                               elevation_view.setText("<unknown>");
+                               range_view.setText("<unknown>");
+                               bearing_view.setText("<unknown>");
+                               compass_view.setText("<unknown>");
+                               distance_view.setText("<unknown>");
+                       }
+                       if (state.gps != null) {
+                               latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+                       }
+
+                       if (state.apogee_voltage == AltosLib.MISSING) {
+                               apogee_view.setVisibility(View.GONE);
+                       } else {
+                               apogee_voltage_view.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+                               apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
+                               apogee_view.setVisibility(View.VISIBLE);
+                       }
+
+                       if (state.main_voltage == AltosLib.MISSING) {
+                               main_view.setVisibility(View.GONE);
+                       } else {
+                               main_voltage_view.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+                               main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
+                               main_view.setVisibility(View.VISIBLE);
+                       }
+               }
+       }
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabMap.java
new file mode 100644 (file)
index 0000000..a2f997f
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+
+public class TabMap extends AltosDroidTab implements AltosDroidMapSourceListener {
+
+       AltosLatLon     here;
+
+       private TextView mDistanceView;
+       private TextView mBearingLabel;
+       private TextView mBearingView;
+       private TextView mTargetLatitudeView;
+       private TextView mTargetLongitudeView;
+       private TextView mReceiverLatitudeView;
+       private TextView mReceiverLongitudeView;
+       private AltosMapOffline map_offline;
+       private AltosMapOnline map_online;
+       private View view;
+       private int map_source;
+
+       @Override
+       public void onAttach(Activity activity) {
+               super.onAttach(activity);
+       }
+
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+       }
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               view = inflater.inflate(R.layout.tab_map, container, false);
+               int map_source = AltosDroidPreferences.map_source();
+
+               mDistanceView  = (TextView)view.findViewById(R.id.distance_value);
+               mBearingLabel  = (TextView)view.findViewById(R.id.bearing_label);
+               mBearingView   = (TextView)view.findViewById(R.id.bearing_value);
+               mTargetLatitudeView  = (TextView)view.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView)view.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
+               map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
+               map_offline.onCreateView(altos_droid);
+               map_online = new AltosMapOnline(view.getContext());
+               map_online.onCreateView(altos_droid);
+               map_source_changed(AltosDroidPreferences.map_source());
+               AltosDroidPreferences.register_map_source_listener(this);
+               return view;
+       }
+
+       @Override
+       public void onActivityCreated(Bundle savedInstanceState) {
+               super.onActivityCreated(savedInstanceState);
+               if (map_online != null)
+                       getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
+       }
+
+       @Override
+       public void onDestroyView() {
+               super.onDestroyView();
+               map_offline.onDestroyView();
+               map_online.onDestroyView();
+               AltosDroidPreferences.unregister_map_source_listener(this);
+       }
+
+       public String tab_name() { return AltosDroid.tab_map_name; }
+
+       private void center(double lat, double lon, double accuracy) {
+               if (map_offline != null)
+                       map_offline.center(lat, lon, accuracy);
+               if (map_online != null)
+                       map_online.center(lat, lon, accuracy);
+       }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (from_receiver != null) {
+                       String  direction = AltosDroid.direction(from_receiver, receiver);
+                       if (direction != null) {
+                               mBearingLabel.setText("Direction");
+                               mBearingView.setText(direction);
+                       } else {
+                               mBearingLabel.setText("Bearing");
+                               mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+                       }
+                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+               } else {
+                       mBearingLabel.setText("Bearing");
+                       mBearingView.setText("");
+                       set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
+               }
+
+               if (state != null) {
+                       if (state.gps != null) {
+                               mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+                       }
+               }
+
+               if (receiver != null) {
+                       double accuracy;
+
+                       here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+                       if (receiver.hasAccuracy())
+                               accuracy = receiver.getAccuracy();
+                       else
+                               accuracy = 1000;
+                       mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
+                       center (here.lat, here.lon, accuracy);
+               }
+               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+                       if (map_offline != null)
+                               map_offline.show(telem_state, state, from_receiver, receiver);
+               } else {
+                       if (map_online != null)
+                               map_online.show(telem_state, state, from_receiver, receiver);
+               }
+       }
+
+       public void map_source_changed(int map_source) {
+               this.map_source = map_source;
+               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+                       if (map_online != null)
+                               map_online.set_visible(false);
+                       if (map_offline != null) {
+                               map_offline.set_visible(true);
+                               map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+                       }
+               } else {
+                       if (map_offline != null)
+                               map_offline.set_visible(false);
+                       if (map_online != null) {
+                               map_online.set_visible(true);
+                               map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+                       }
+               }
+       }
+
+       public TabMap() {
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabPad.java
new file mode 100644 (file)
index 0000000..f317ae9
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+
+public class TabPad extends AltosDroidTab {
+       private TextView battery_voltage_view;
+       private GoNoGoLights battery_lights;
+
+       private TableRow receiver_row;
+       private TextView receiver_voltage_view;
+       private TextView receiver_voltage_label;
+       private GoNoGoLights receiver_voltage_lights;
+
+       private TableRow apogee_row;
+       private TextView apogee_voltage_view;
+       private TextView apogee_voltage_label;
+       private GoNoGoLights apogee_lights;
+
+       private TableRow main_row;
+       private TextView main_voltage_view;
+       private TextView main_voltage_label;
+       private GoNoGoLights main_lights;
+
+       private TextView data_logging_view;
+       private GoNoGoLights data_logging_lights;
+
+       private TextView gps_locked_view;
+       private GoNoGoLights gps_locked_lights;
+
+       private TextView gps_ready_view;
+       private GoNoGoLights gps_ready_lights;
+
+       private TextView receiver_latitude_view;
+       private TextView receiver_longitude_view;
+       private TextView receiver_altitude_view;
+
+       private TableRow[] ignite_row = new TableRow[4];
+       private TextView[] ignite_voltage_view = new TextView[4];
+       private TextView[] ignite_voltage_label = new TextView[4];
+       private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
+
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               View v = inflater.inflate(R.layout.tab_pad, container, false);
+               battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
+               battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
+                                                 (ImageView) v.findViewById(R.id.battery_greenled),
+                                                 getResources());
+
+               receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
+               receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
+               receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
+               receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
+                                                          (ImageView) v.findViewById(R.id.receiver_greenled),
+                                                          getResources());
+
+               apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
+               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+                                                (ImageView) v.findViewById(R.id.apogee_greenled),
+                                                getResources());
+
+               main_row = (TableRow) v.findViewById(R.id.main_row);
+               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+                                              (ImageView) v.findViewById(R.id.main_greenled),
+                                              getResources());
+
+               data_logging_view = (TextView) v.findViewById(R.id.logging_value);
+               data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
+                                                     (ImageView) v.findViewById(R.id.logging_greenled),
+                                                     getResources());
+
+               gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
+               gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
+                                                   (ImageView) v.findViewById(R.id.gps_locked_greenled),
+                                                   getResources());
+
+               gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
+               gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
+                                                  (ImageView) v.findViewById(R.id.gps_ready_greenled),
+                                                  getResources());
+
+               for (int i = 0; i < 4; i++) {
+                       int row_id, view_id, label_id, lights_id;
+                       int red_id, green_id;
+                       switch (i) {
+                       case 0:
+                       default:
+                               row_id = R.id.ignite_a_row;
+                               view_id = R.id.ignite_a_voltage_value;
+                               label_id = R.id.ignite_a_voltage_label;
+                               red_id = R.id.ignite_a_redled;
+                               green_id = R.id.ignite_a_greenled;
+                               break;
+                       case 1:
+                               row_id = R.id.ignite_b_row;
+                               view_id = R.id.ignite_b_voltage_value;
+                               label_id = R.id.ignite_b_voltage_label;
+                               red_id = R.id.ignite_b_redled;
+                               green_id = R.id.ignite_b_greenled;
+                               break;
+                       case 2:
+                               row_id = R.id.ignite_c_row;
+                               view_id = R.id.ignite_c_voltage_value;
+                               label_id = R.id.ignite_c_voltage_label;
+                               red_id = R.id.ignite_c_redled;
+                               green_id = R.id.ignite_c_greenled;
+                               break;
+                       case 3:
+                               row_id = R.id.ignite_d_row;
+                               view_id = R.id.ignite_d_voltage_value;
+                               label_id = R.id.ignite_d_voltage_label;
+                               red_id = R.id.ignite_d_redled;
+                               green_id = R.id.ignite_d_greenled;
+                               break;
+                       }
+                       ignite_row[i] = (TableRow) v.findViewById(row_id);
+                       ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
+                       ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
+                       ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
+                                                            (ImageView) v.findViewById(green_id),
+                                                            getResources());
+               }
+
+               receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
+               receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
+               receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
+        return v;
+       }
+
+       public String tab_name() { return AltosDroid.tab_pad_name; }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (state != null) {
+                       battery_voltage_view.setText(AltosDroid.number(" %4.2f V", state.battery_voltage));
+                       battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
+                       if (state.apogee_voltage == AltosLib.MISSING) {
+                               apogee_row.setVisibility(View.GONE);
+                       } else {
+                               apogee_voltage_view.setText(AltosDroid.number(" %4.2f V", state.apogee_voltage));
+                               apogee_row.setVisibility(View.VISIBLE);
+                       }
+                       apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
+                       if (state.main_voltage == AltosLib.MISSING) {
+                               main_row.setVisibility(View.GONE);
+                       } else {
+                               main_voltage_view.setText(AltosDroid.number(" %4.2f V", state.main_voltage));
+                               main_row.setVisibility(View.VISIBLE);
+                       }
+                       main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
+
+                       int num_igniter = state.igniter_voltage == null ? 0 : state.igniter_voltage.length;
+
+                       for (int i = 0; i < 4; i++) {
+                               double voltage = i >= num_igniter ? AltosLib.MISSING : state.igniter_voltage[i];
+                               if (voltage == AltosLib.MISSING) {
+                                       ignite_row[i].setVisibility(View.GONE);
+                               } else {
+                                       ignite_voltage_view[i].setText(AltosDroid.number(" %4.2f V", voltage));
+                                       ignite_row[i].setVisibility(View.VISIBLE);
+                               }
+                               ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
+                       }
+
+                       if (state.cal_data().flight != 0) {
+                               if (state.state() <= AltosLib.ao_flight_pad)
+                                       data_logging_view.setText("Ready to record");
+                               else if (state.state() < AltosLib.ao_flight_landed)
+                                       data_logging_view.setText("Recording data");
+                               else
+                                       data_logging_view.setText("Recorded data");
+                       } else {
+                               data_logging_view.setText("Storage full");
+                       }
+                       data_logging_lights.set(state.cal_data().flight != 0, state.cal_data().flight == AltosLib.MISSING);
+
+                       if (state.gps != null) {
+                               int soln = state.gps.nsat;
+                               int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
+                               gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
+                               gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
+                               if (state.gps_ready)
+                                       gps_ready_view.setText("Ready");
+                               else
+                                       gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
+                       } else
+                               gps_locked_lights.set(false, true);
+                       gps_ready_lights.set(state.gps_ready, state.gps == null);
+               }
+
+               if (telem_state != null) {
+                       if (telem_state.receiver_battery == AltosLib.MISSING) {
+                               receiver_row.setVisibility(View.GONE);
+                       } else {
+                               receiver_voltage_view.setText(AltosDroid.number(" %4.2f V", telem_state.receiver_battery));
+                               receiver_row.setVisibility(View.VISIBLE);
+                       }
+                       receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
+               }
+
+               if (receiver != null) {
+                       double altitude = AltosLib.MISSING;
+                       if (receiver.hasAltitude())
+                               altitude = receiver.getAltitude();
+                       receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+                       set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabRecover.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabRecover.java
new file mode 100644 (file)
index 0000000..3df4838
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+
+public class TabRecover extends AltosDroidTab {
+       private TextView mBearingView;
+       private TextView mDirectionView;
+       private TextView mDistanceView;
+       private TextView mTargetLatitudeView;
+       private TextView mTargetLongitudeView;
+       private TextView mReceiverLatitudeView;
+       private TextView mReceiverLongitudeView;
+       private TextView mMaxHeightView;
+       private TextView mMaxSpeedView;
+       private TextView mMaxAccelView;
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               View v = inflater.inflate(R.layout.tab_recover, container, false);
+
+               mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
+               mDirectionView = (TextView) v.findViewById(R.id.direction_value);
+               mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
+               mTargetLatitudeView  = (TextView) v.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView) v.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
+               mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
+               mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
+               mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
+
+               return v;
+       }
+
+       public String tab_name() { return AltosDroid.tab_recover_name; }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (from_receiver != null) {
+                       mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+                       String direction = AltosDroid.direction(from_receiver, receiver);
+                       if (direction == null)
+                               mDirectionView.setText("");
+                       else
+                               mDirectionView.setText(direction);
+               }
+               if (state != null && state.gps != null) {
+                       mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                       mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+               }
+
+               if (receiver != null) {
+                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+               }
+
+               if (state != null) {
+                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
+                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
+               }
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabsAdapter.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TabsAdapter.java
new file mode 100644 (file)
index 0000000..b34a25b
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+
+/**
+ * This is a helper class that implements the management of tabs and all
+ * details of connecting a ViewPager with associated TabHost.  It relies on a
+ * trick.  Normally a tab host has a simple API for supplying a View or
+ * Intent that each tab will show.  This is not sufficient for switching
+ * between pages.  So instead we make the content part of the tab host
+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
+ * view to show as the tab content.  It listens to changes in tabs, and takes
+ * care of switch to the correct paged in the ViewPager whenever the selected
+ * tab changes.
+ */
+public class TabsAdapter extends FragmentPagerAdapter
+               implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
+       private final Context mContext;
+       private final TabHost mTabHost;
+       private final ViewPager mViewPager;
+       private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+       private int position;
+
+       static class TabInfo {
+               private final String tag;
+               private final Class<?> clss;
+               private final Bundle args;
+               private Fragment fragment;
+
+               TabInfo(String _tag, Class<?> _class, Bundle _args) {
+                       tag = _tag;
+                       clss = _class;
+                       args = _args;
+               }
+       }
+
+       static class DummyTabFactory implements TabHost.TabContentFactory {
+               private final Context mContext;
+
+               public DummyTabFactory(Context context) {
+                       mContext = context;
+               }
+
+               public View createTabContent(String tag) {
+                       View v = new View(mContext);
+                       v.setMinimumWidth(0);
+                       v.setMinimumHeight(0);
+                       return v;
+               }
+       }
+
+       public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
+               super(activity.getSupportFragmentManager());
+               mContext = activity;
+               mTabHost = tabHost;
+               mViewPager = pager;
+               mTabHost.setOnTabChangedListener(this);
+               mViewPager.setAdapter(this);
+               mViewPager.setOnPageChangeListener(this);
+       }
+
+       public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
+               tabSpec.setContent(new DummyTabFactory(mContext));
+               String tag = tabSpec.getTag();
+
+               TabInfo info = new TabInfo(tag, clss, args);
+               mTabs.add(info);
+               mTabHost.addTab(tabSpec);
+               notifyDataSetChanged();
+       }
+
+       @Override
+       public int getCount() {
+               return mTabs.size();
+       }
+
+       @Override
+       public Fragment getItem(int position) {
+               TabInfo info = mTabs.get(position);
+               AltosDebug.debug("TabsAdapter.getItem(%d)", position);
+               info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
+               return info.fragment;
+       }
+
+       public Fragment currentItem() {
+               TabInfo info = mTabs.get(position);
+               return info.fragment;
+       }
+
+       public void onTabChanged(String tabId) {
+               AltosDroidTab   prev_frag = (AltosDroidTab) mTabs.get(position).fragment;
+
+               position = mTabHost.getCurrentTab();
+
+               AltosDroidTab   cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
+
+               if (prev_frag != cur_frag) {
+                       if (prev_frag != null) {
+                               prev_frag.set_visible(false);
+                       }
+               }
+               if (cur_frag != null) {
+                       cur_frag.set_visible(true);
+               }
+               AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position);
+               mViewPager.setCurrentItem(position);
+       }
+
+       public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+       }
+
+       public void onPageSelected(int position) {
+               // Unfortunately when TabHost changes the current tab, it kindly
+               // also takes care of putting focus on it when not in touch mode.
+               // The jerk.
+               // This hack tries to prevent this from pulling focus out of our
+               // ViewPager.
+               TabWidget widget = mTabHost.getTabWidget();
+               int oldFocusability = widget.getDescendantFocusability();
+               widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+               mTabHost.setCurrentTab(position);
+               widget.setDescendantFocusability(oldFocusability);
+       }
+
+       public void onPageScrollStateChanged(int state) {
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryLogger.java
new file mode 100644 (file)
index 0000000..49ba547
--- /dev/null
@@ -0,0 +1,69 @@
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_13.*;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Environment;
+
+public class TelemetryLogger {
+       private Context   context = null;
+       private AltosLink link    = null;
+       private AltosLog  logger  = null;
+
+       private BroadcastReceiver mExternalStorageReceiver;
+
+       public TelemetryLogger(Context in_context, AltosLink in_link) {
+               context = in_context;
+               link    = in_link;
+
+               startWatchingExternalStorage();
+       }
+
+       public void stop() {
+               stopWatchingExternalStorage();
+               close();
+       }
+
+       private void close() {
+               if (logger != null) {
+                       AltosDebug.debug("Shutting down Telemetry Logging");
+                       logger.close();
+                       logger = null;
+               }
+       }
+
+       void handleExternalStorageState() {
+               String state = Environment.getExternalStorageState();
+               if (Environment.MEDIA_MOUNTED.equals(state)) {
+                       if (logger == null) {
+                               AltosDebug.debug("Starting up Telemetry Logging");
+                               logger = new AltosLog(link);
+                       }
+               } else {
+                       AltosDebug.debug("External Storage not present - stopping");
+                       close();
+               }
+       }
+
+       void startWatchingExternalStorage() {
+               mExternalStorageReceiver = new BroadcastReceiver() {
+                       @Override
+                       public void onReceive(Context context, Intent intent) {
+                               handleExternalStorageState();
+                       }
+               };
+               IntentFilter filter = new IntentFilter();
+               filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+               filter.addAction(Intent.ACTION_MEDIA_REMOVED);
+               context.registerReceiver(mExternalStorageReceiver, filter);
+               handleExternalStorageState();
+       }
+
+       void stopWatchingExternalStorage() {
+               context.unregisterReceiver(mExternalStorageReceiver);
+       }
+
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryReader.java
new file mode 100644 (file)
index 0000000..5cfa647
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+
+package org.altusmetrum.AltosDroid;
+
+import java.text.*;
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_13.*;
+
+
+public class TelemetryReader extends Thread {
+
+       int         crc_errors;
+
+       Handler     handler;
+
+       AltosLink   link;
+
+       LinkedBlockingQueue<AltosLine> telemQueue;
+
+       public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
+               AltosLine l = telemQueue.take();
+               if (l.line == null)
+                       throw new IOException("IO error");
+               AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
+               return telem;
+       }
+
+       public void close() {
+               link.remove_monitor(telemQueue);
+               link = null;
+               telemQueue.clear();
+               telemQueue = null;
+       }
+
+       public void run() {
+               try {
+                       AltosDebug.debug("starting loop");
+                       while (telemQueue != null) {
+                               try {
+                                       AltosTelemetry  telem = read();
+                                       telem.set_frequency(link.frequency);
+                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
+                               } catch (ParseException pp) {
+                                       AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
+                               } catch (AltosCRCException ce) {
+                                       ++crc_errors;
+                                       handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
+                               }
+                       }
+               } catch (InterruptedException ee) {
+               } catch (IOException ie) {
+                       AltosDebug.error("IO exception in telemetry reader");
+                       handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
+               } finally {
+                       close();
+               }
+       }
+
+       public TelemetryReader (AltosLink in_link, Handler in_handler) {
+               AltosDebug.debug("connected TelemetryReader create started");
+               link    = in_link;
+               handler = in_handler;
+
+               telemQueue = new LinkedBlockingQueue<AltosLine>();
+               link.add_monitor(telemQueue);
+               link.set_telemetry(AltosLib.ao_telemetry_standard);
+
+               AltosDebug.debug("connected TelemetryReader created");
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java
new file mode 100644 (file)
index 0000000..22a2bbd
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.TimeoutException;
+import java.util.*;
+
+import android.app.Notification;
+//import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.hardware.usb.*;
+import android.content.Intent;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.Looper;
+import android.widget.Toast;
+import android.location.Criteria;
+
+import org.altusmetrum.altoslib_13.*;
+
+public class TelemetryService extends Service implements AltosIdleMonitorListener {
+
+       static final int MSG_REGISTER_CLIENT   = 1;
+       static final int MSG_UNREGISTER_CLIENT = 2;
+       static final int MSG_CONNECT           = 3;
+       static final int MSG_OPEN_USB          = 4;
+       static final int MSG_CONNECTED         = 5;
+       static final int MSG_CONNECT_FAILED    = 6;
+       static final int MSG_DISCONNECTED      = 7;
+       static final int MSG_TELEMETRY         = 8;
+       static final int MSG_SETFREQUENCY      = 9;
+       static final int MSG_CRC_ERROR         = 10;
+       static final int MSG_SETBAUD           = 11;
+       static final int MSG_DISCONNECT        = 12;
+       static final int MSG_DELETE_SERIAL     = 13;
+       static final int MSG_BLUETOOTH_ENABLED = 14;
+       static final int MSG_MONITOR_IDLE_START= 15;
+       static final int MSG_MONITOR_IDLE_STOP = 16;
+       static final int MSG_REBOOT            = 17;
+       static final int MSG_IGNITER_QUERY     = 18;
+       static final int MSG_IGNITER_FIRE      = 19;
+
+       // Unique Identification Number for the Notification.
+       // We use it on Notification start, and to cancel it.
+       private int NOTIFICATION = R.string.telemetry_service_label;
+       //private NotificationManager mNM;
+
+       ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
+       final Handler   handler   = new IncomingHandler(this);
+       final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.
+
+       // Name of the connected device
+       DeviceAddress address;
+       private AltosDroidLink  altos_link  = null;
+       private TelemetryReader telemetry_reader = null;
+       private TelemetryLogger telemetry_logger = null;
+
+       // Local Bluetooth adapter
+       private BluetoothAdapter bluetooth_adapter = null;
+
+       // Last data seen; send to UI when it starts
+       private TelemetryState  telemetry_state;
+
+       // Idle monitor if active
+       AltosIdleMonitor idle_monitor = null;
+
+       // Igniter bits
+       AltosIgnite ignite = null;
+       boolean ignite_running;
+
+       // Handler of incoming messages from clients.
+       static class IncomingHandler extends Handler {
+               private final WeakReference<TelemetryService> service;
+               IncomingHandler(TelemetryService s) { service = new WeakReference<TelemetryService>(s); }
+
+               @Override
+               public void handleMessage(Message msg) {
+                       DeviceAddress address;
+
+                       TelemetryService s = service.get();
+                       AltosDroidLink bt = null;
+                       if (s == null)
+                               return;
+
+                       switch (msg.what) {
+
+                               /* Messages from application */
+                       case MSG_REGISTER_CLIENT:
+                               s.add_client(msg.replyTo);
+                               break;
+                       case MSG_UNREGISTER_CLIENT:
+                               s.remove_client(msg.replyTo);
+                               break;
+                       case MSG_CONNECT:
+                               AltosDebug.debug("Connect command received");
+                               address = (DeviceAddress) msg.obj;
+                               AltosDroidPreferences.set_active_device(address);
+                               s.start_altos_bluetooth(address, false);
+                               break;
+                       case MSG_OPEN_USB:
+                               AltosDebug.debug("Open USB command received");
+                               UsbDevice device = (UsbDevice) msg.obj;
+                               s.start_usb(device);
+                               break;
+                       case MSG_DISCONNECT:
+                               AltosDebug.debug("Disconnect command received");
+                               s.address = null;
+                               if (!(Boolean) msg.obj)
+                                       AltosDroidPreferences.set_active_device(null);
+                               s.disconnect(true);
+                               break;
+                       case MSG_DELETE_SERIAL:
+                               AltosDebug.debug("Delete Serial command received");
+                               s.delete_serial((Integer) msg.obj);
+                               break;
+                       case MSG_SETFREQUENCY:
+                               AltosDebug.debug("MSG_SETFREQUENCY");
+                               s.telemetry_state.frequency = (Double) msg.obj;
+                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
+                                       try {
+                                               s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
+                                               s.altos_link.save_frequency();
+                                       } catch (InterruptedException e) {
+                                       } catch (TimeoutException e) {
+                                       }
+                               }
+                               s.send_to_clients();
+                               break;
+                       case MSG_SETBAUD:
+                               AltosDebug.debug("MSG_SETBAUD");
+                               s.telemetry_state.telemetry_rate = (Integer) msg.obj;
+                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
+                                       s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+                                       s.altos_link.save_telemetry_rate();
+                               }
+                               s.send_to_clients();
+                               break;
+
+                               /*
+                                *Messages from AltosBluetooth
+                                */
+                       case MSG_CONNECTED:
+                               AltosDebug.debug("MSG_CONNECTED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               AltosDebug.debug("Connected to device");
+                               try {
+                                       s.connected();
+                               } catch (InterruptedException ie) {
+                               }
+                               break;
+                       case MSG_CONNECT_FAILED:
+                               AltosDebug.debug("MSG_CONNECT_FAILED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               if (s.address != null) {
+                                       AltosDebug.debug("Connection failed... retrying");
+                                       s.start_altos_bluetooth(s.address, true);
+                               } else {
+                                       s.disconnect(true);
+                               }
+                               break;
+                       case MSG_DISCONNECTED:
+
+                               /* This can be sent by either AltosDroidLink or TelemetryReader */
+                               AltosDebug.debug("MSG_DISCONNECTED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               if (s.address != null) {
+                                       AltosDebug.debug("Connection lost... retrying");
+                                       s.start_altos_bluetooth(s.address, true);
+                               } else {
+                                       s.disconnect(true);
+                               }
+                               break;
+
+                               /*
+                                * Messages from TelemetryReader
+                                */
+                       case MSG_TELEMETRY:
+                               s.telemetry((AltosTelemetry) msg.obj);
+                               break;
+                       case MSG_CRC_ERROR:
+                               // forward crc error messages
+                               s.telemetry_state.crc_errors = (Integer) msg.obj;
+                               s.send_to_clients();
+                               break;
+                       case MSG_BLUETOOTH_ENABLED:
+                               AltosDebug.debug("TelemetryService notes that BT is now enabled");
+                               address = AltosDroidPreferences.active_device();
+                               if (address != null && !address.address.startsWith("USB"))
+                                       s.start_altos_bluetooth(address, false);
+                               break;
+                       case MSG_MONITOR_IDLE_START:
+                               AltosDebug.debug("start monitor idle");
+                               s.start_idle_monitor();
+                               break;
+                       case MSG_MONITOR_IDLE_STOP:
+                               AltosDebug.debug("stop monitor idle");
+                               s.stop_idle_monitor();
+                               break;
+                       case MSG_REBOOT:
+                               AltosDebug.debug("reboot");
+                               s.reboot_remote();
+                               break;
+                       case MSG_IGNITER_QUERY:
+                               AltosDebug.debug("igniter query");
+                               s.igniter_query(msg.replyTo);
+                               break;
+                       case MSG_IGNITER_FIRE:
+                               AltosDebug.debug("igniter fire");
+                               s.igniter_fire((String) msg.obj);
+                               break;
+                       default:
+                               super.handleMessage(msg);
+                       }
+               }
+       }
+
+       /* Handle telemetry packet
+        */
+       private void telemetry(AltosTelemetry telem) {
+               AltosState      state;
+
+               if (telemetry_state.states.containsKey(telem.serial()))
+                       state = telemetry_state.states.get(telem.serial());
+               else
+                       state = new AltosState(new AltosCalData());
+               telem.provide_data(state);
+               telemetry_state.states.put(telem.serial(), state);
+               telemetry_state.quiet = false;
+               if (state != null) {
+                       AltosPreferences.set_state(state,telem.serial());
+               }
+               send_to_clients();
+       }
+
+       /* Construct the message to deliver to clients
+        */
+       private Message message() {
+               if (telemetry_state == null)
+                       AltosDebug.debug("telemetry_state null!");
+               if (telemetry_state.states == null)
+                       AltosDebug.debug("telemetry_state.states null!");
+               return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
+       }
+
+       /* A new friend has connected
+        */
+       private void add_client(Messenger client) {
+
+               clients.add(client);
+               AltosDebug.debug("Client bound to service");
+
+               /* On connect, send the current state to the new client
+                */
+               send_to_client(client);
+               send_idle_mode_to_client(client);
+
+               /* If we've got an address from a previous session, then
+                * go ahead and try to reconnect to the device
+                */
+               if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
+                       AltosDebug.debug("Reconnecting now...");
+                       start_altos_bluetooth(address, false);
+               }
+       }
+
+       /* A client has disconnected, clean up
+        */
+       private void remove_client(Messenger client) {
+               clients.remove(client);
+               AltosDebug.debug("Client unbound from service");
+
+               /* When the list of clients is empty, stop the service if
+                * we have no current telemetry source
+                */
+
+                if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
+                        AltosDebug.debug("No clients, no connection. Stopping\n");
+                        stopSelf();
+                }
+       }
+
+       private void send_to_client(Messenger client) {
+               Message m = message();
+               try {
+                       client.send(m);
+               } catch (RemoteException e) {
+                       AltosDebug.error("Client %s disappeared", client.toString());
+                       remove_client(client);
+               }
+       }
+
+       private void send_to_clients() {
+               for (Messenger client : clients)
+                       send_to_client(client);
+       }
+
+       private void send_idle_mode_to_client(Messenger client) {
+               Message m = Message.obtain(null, AltosDroid.MSG_IDLE_MODE, idle_monitor != null);
+               try {
+                       client.send(m);
+               } catch (RemoteException e) {
+                       AltosDebug.error("Client %s disappeared", client.toString());
+                       remove_client(client);
+               }
+       }
+
+       private void send_idle_mode_to_clients() {
+               for (Messenger client : clients)
+                       send_idle_mode_to_client(client);
+       }
+
+       private void telemetry_start() {
+               if (telemetry_reader == null && idle_monitor == null && !ignite_running) {
+                       telemetry_reader = new TelemetryReader(altos_link, handler);
+                       telemetry_reader.start();
+               }
+       }
+
+       private void telemetry_stop() {
+               if (telemetry_reader != null) {
+                       AltosDebug.debug("disconnect(): stopping TelemetryReader");
+                       telemetry_reader.interrupt();
+                       try {
+                               telemetry_reader.join();
+                       } catch (InterruptedException e) {
+                       }
+                       telemetry_reader = null;
+               }
+       }
+
+       private void disconnect(boolean notify) {
+               AltosDebug.debug("disconnect(): begin");
+
+               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
+               telemetry_state.address = null;
+
+               if (idle_monitor != null)
+                       stop_idle_monitor();
+
+               if (altos_link != null)
+                       altos_link.closing();
+
+               stop_receiver_voltage_timer();
+
+               telemetry_stop();
+               if (telemetry_logger != null) {
+                       AltosDebug.debug("disconnect(): stopping TelemetryLogger");
+                       telemetry_logger.stop();
+                       telemetry_logger = null;
+               }
+               if (altos_link != null) {
+                       AltosDebug.debug("disconnect(): stopping AltosDroidLink");
+                       altos_link.close();
+                       altos_link = null;
+                       ignite = null;
+               }
+               telemetry_state.config = null;
+               if (notify) {
+                       AltosDebug.debug("disconnect(): send message to clients");
+                       send_to_clients();
+                       if (clients.isEmpty()) {
+                               AltosDebug.debug("disconnect(): no clients, terminating");
+                               stopSelf();
+                       }
+               }
+       }
+
+       private void start_usb(UsbDevice device) {
+               AltosUsb        d = new AltosUsb(this, device, handler);
+
+               if (d != null) {
+                       disconnect(false);
+                       altos_link = d;
+                       try {
+                               connected();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       private void delete_serial(int serial) {
+               telemetry_state.states.remove((Integer) serial);
+               AltosPreferences.remove_state(serial);
+               send_to_clients();
+       }
+
+       private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
+               if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled())
+                       return;
+
+               disconnect(false);
+
+               // Get the BluetoothDevice object
+               BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
+
+               this.address = address;
+               AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
+               altos_link = new AltosBluetooth(device, handler, pause);
+               telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
+               telemetry_state.address = address;
+               send_to_clients();
+       }
+
+       private void start_idle_monitor() {
+               if (altos_link != null && idle_monitor == null) {
+                       telemetry_stop();
+                       idle_monitor = new AltosIdleMonitor(this, altos_link, true, false);
+                       idle_monitor.set_callsign(AltosPreferences.callsign());
+                       idle_monitor.start();
+                       send_idle_mode_to_clients();
+               }
+       }
+
+       private void stop_idle_monitor() {
+               if (idle_monitor != null) {
+                       try {
+                               idle_monitor.abort();
+                       } catch (InterruptedException ie) {
+                       }
+                       idle_monitor = null;
+                       telemetry_start();
+                       send_idle_mode_to_clients();
+               }
+       }
+
+       private void reboot_remote() {
+               if (altos_link != null) {
+                       stop_idle_monitor();
+                       try {
+                               altos_link.start_remote();
+                               altos_link.printf("r eboot\n");
+                               altos_link.flush_output();
+                       } catch (TimeoutException te) {
+                       } catch (InterruptedException ie) {
+                       } finally {
+                               try {
+                                       altos_link.stop_remote();
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+               }
+       }
+
+       private void ensure_ignite() {
+               if (ignite == null)
+                       ignite = new AltosIgnite(altos_link, true, false);
+       }
+
+       private synchronized void igniter_query(Messenger client) {
+               ensure_ignite();
+               HashMap<String,Integer> status_map = null;
+               ignite_running = true;
+               try {
+                       stop_idle_monitor();
+                       try {
+                               status_map = ignite.status();
+                       } catch (InterruptedException ie) {
+                               AltosDebug.debug("ignite.status interrupted");
+                       } catch (TimeoutException te) {
+                               AltosDebug.debug("ignite.status timeout");
+                       }
+               } finally {
+                       ignite_running = false;
+               }
+               Message m = Message.obtain(null, AltosDroid.MSG_IGNITER_STATUS, status_map);
+               try {
+                       client.send(m);
+               } catch (RemoteException e) {
+               }
+       }
+
+       private synchronized void igniter_fire(String igniter) {
+               ensure_ignite();
+               ignite_running = true;
+               stop_idle_monitor();
+               try {
+                       ignite.fire(igniter);
+               } catch (InterruptedException ie) {
+               } finally {
+                       ignite_running = false;
+               }
+       }
+
+       // Timer for receiver battery voltage monitoring
+       Timer receiver_voltage_timer;
+
+       private void update_receiver_voltage() {
+               if (altos_link != null && idle_monitor == null && !ignite_running) {
+                       try {
+                               double  voltage = altos_link.monitor_battery();
+                               telemetry_state.receiver_battery = voltage;
+                               send_to_clients();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       private void stop_receiver_voltage_timer() {
+               if (receiver_voltage_timer != null) {
+                       receiver_voltage_timer.cancel();
+                       receiver_voltage_timer.purge();
+                       receiver_voltage_timer = null;
+               }
+       }
+
+       private void start_receiver_voltage_timer() {
+               if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
+                       receiver_voltage_timer = new Timer();
+                       receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
+               }
+       }
+
+       private void connected() throws InterruptedException {
+               AltosDebug.debug("connected top");
+               AltosDebug.check_ui("connected\n");
+               try {
+                       if (altos_link == null)
+                               throw new InterruptedException("no bluetooth");
+                       telemetry_state.config = altos_link.config_data();
+                       altos_link.set_radio_frequency(telemetry_state.frequency);
+                       altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
+               } catch (TimeoutException e) {
+                       // If this timed out, then we really want to retry it, but
+                       // probably safer to just retry the connection from scratch.
+                       AltosDebug.debug("connected timeout");
+                       if (address != null) {
+                               AltosDebug.debug("connected timeout, retrying");
+                               start_altos_bluetooth(address, true);
+                       } else {
+                               handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
+                               disconnect(true);
+                       }
+                       return;
+               }
+
+               AltosDebug.debug("connected bluetooth configured");
+               telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
+               telemetry_state.address = address;
+
+               telemetry_start();
+
+               AltosDebug.debug("connected TelemetryReader started");
+
+               telemetry_logger = new TelemetryLogger(this, altos_link);
+
+               start_receiver_voltage_timer();
+
+               AltosDebug.debug("Notify UI of connection");
+
+               send_to_clients();
+       }
+
+
+       @Override
+       public void onCreate() {
+
+               AltosDebug.init(this);
+
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
+
+               // Get local Bluetooth adapter
+               bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
+
+               telemetry_state = new TelemetryState();
+
+               // Create a reference to the NotificationManager so that we can update our notifcation text later
+               //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+
+               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
+               telemetry_state.address = null;
+
+               /* Pull the saved state information out of the preferences database
+                */
+               ArrayList<Integer> serials = AltosPreferences.list_states();
+
+               telemetry_state.latest_serial = AltosPreferences.latest_state();
+
+               telemetry_state.quiet = true;
+
+               AltosDebug.debug("latest serial %d\n", telemetry_state.latest_serial);
+
+               for (int serial : serials) {
+                       AltosState saved_state = AltosPreferences.state(serial);
+                       if (saved_state != null) {
+                               if (telemetry_state.latest_serial == 0)
+                                       telemetry_state.latest_serial = serial;
+
+                               AltosDebug.debug("recovered old state serial %d flight %d",
+                                                serial,
+                                                saved_state.cal_data().flight);
+                               if (saved_state.gps != null)
+                                       AltosDebug.debug("\tposition %f,%f",
+                                                        saved_state.gps.lat,
+                                                        saved_state.gps.lon);
+                               telemetry_state.states.put(serial, saved_state);
+                       } else {
+                               AltosDebug.debug("Failed to recover state for %d", serial);
+                               AltosPreferences.remove_state(serial);
+                       }
+               }
+       }
+
+       @Override
+       public int onStartCommand(Intent intent, int flags, int startId) {
+               AltosDebug.debug("Received start id %d: %s", startId, intent);
+
+               CharSequence text = getText(R.string.telemetry_service_started);
+
+               // Create notification to be displayed while the service runs
+               Notification notification = new Notification(R.drawable.am_status_c, text, 0);
+
+               // The PendingIntent to launch our activity if the user selects this notification
+               PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+                               new Intent(this, AltosDroid.class), 0);
+
+               // Set the info for the views that show in the notification panel.
+               notification.setLatestEventInfo(this, getText(R.string.telemetry_service_label), text, contentIntent);
+
+               // Set the notification to be in the "Ongoing" section.
+               notification.flags |= Notification.FLAG_ONGOING_EVENT;
+
+               // Move us into the foreground.
+               startForeground(NOTIFICATION, notification);
+
+               /* Start bluetooth if we don't have a connection already */
+               if (intent != null &&
+                   (telemetry_state.connect == TelemetryState.CONNECT_NONE ||
+                    telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
+               {
+                       String  action = intent.getAction();
+
+                       if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
+                               DeviceAddress address = AltosDroidPreferences.active_device();
+                               if (address != null && !address.address.startsWith("USB"))
+                                       start_altos_bluetooth(address, false);
+                       }
+               }
+
+               // We want this service to continue running until it is explicitly
+               // stopped, so return sticky.
+               return START_STICKY;
+       }
+
+       @Override
+       public void onDestroy() {
+
+               // Stop the bluetooth Comms threads
+               disconnect(true);
+
+               // Demote us from the foreground, and cancel the persistent notification.
+               stopForeground(true);
+
+               // Tell the user we stopped.
+               Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();
+       }
+
+       @Override
+       public IBinder onBind(Intent intent) {
+               return messenger.getBinder();
+       }
+
+       /* AltosIdleMonitorListener */
+       public void update(AltosState state, AltosListenerState listener_state) {
+               telemetry_state.states.put(state.cal_data().serial, state);
+               telemetry_state.receiver_battery = listener_state.battery;
+               send_to_clients();
+       }
+
+       public void failed() {
+       }
+
+       public void error(String reason) {
+               stop_idle_monitor();
+       }
+}
diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java
new file mode 100644 (file)
index 0000000..74d5cd2
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import org.altusmetrum.altoslib_13.*;
+import android.location.Location;
+
+public class TelemetryState {
+       public static final int CONNECT_NONE         = 0;
+       public static final int CONNECT_DISCONNECTED = 1;
+       public static final int CONNECT_CONNECTING   = 2;
+       public static final int CONNECT_CONNECTED    = 3;
+
+       int             connect;
+       DeviceAddress   address;
+       AltosConfigData config;
+       int             crc_errors;
+       double          receiver_battery;
+       double          frequency;
+       int             telemetry_rate;
+
+       boolean         quiet;
+
+       HashMap<Integer,AltosState>     states;
+
+       int             latest_serial;
+
+       public TelemetryState() {
+               connect = CONNECT_NONE;
+               config = null;
+               states = new HashMap<Integer,AltosState>();
+               crc_errors = 0;
+               receiver_battery = AltosLib.MISSING;
+               frequency = AltosPreferences.frequency(0);
+               telemetry_rate = AltosPreferences.telemetry_rate(0);
+       }
+}
diff --git a/altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png b/altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png
new file mode 100644 (file)
index 0000000..d439321
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png differ
diff --git a/altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png b/altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png
new file mode 100644 (file)
index 0000000..03f9dd7
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png differ
diff --git a/altosdroid/app/src/main/res/drawable-hdpi/app_icon.png b/altosdroid/app/src/main/res/drawable-hdpi/app_icon.png
new file mode 100644 (file)
index 0000000..da2f08a
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-hdpi/app_icon.png differ
diff --git a/altosdroid/app/src/main/res/drawable-hdpi/ic_maps_indicator_current_position.png b/altosdroid/app/src/main/res/drawable-hdpi/ic_maps_indicator_current_position.png
new file mode 100644 (file)
index 0000000..bc9160d
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-hdpi/ic_maps_indicator_current_position.png differ
diff --git a/altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png b/altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png
new file mode 100644 (file)
index 0000000..30a8d29
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png differ
diff --git a/altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png b/altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png
new file mode 100644 (file)
index 0000000..07f7f07
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png differ
diff --git a/altosdroid/app/src/main/res/drawable-mdpi/ic_maps_indicator_current_position.png b/altosdroid/app/src/main/res/drawable-mdpi/ic_maps_indicator_current_position.png
new file mode 100644 (file)
index 0000000..4e427d8
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable-mdpi/ic_maps_indicator_current_position.png differ
diff --git a/altosdroid/app/src/main/res/drawable/app_icon.png b/altosdroid/app/src/main/res/drawable/app_icon.png
new file mode 100644 (file)
index 0000000..b0a3c9c
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable/app_icon.png differ
diff --git a/altosdroid/app/src/main/res/drawable/pad.png b/altosdroid/app/src/main/res/drawable/pad.png
new file mode 100644 (file)
index 0000000..b2e65c8
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable/pad.png differ
diff --git a/altosdroid/app/src/main/res/drawable/rocket.png b/altosdroid/app/src/main/res/drawable/rocket.png
new file mode 100644 (file)
index 0000000..7e62f6c
Binary files /dev/null and b/altosdroid/app/src/main/res/drawable/rocket.png differ
diff --git a/altosdroid/app/src/main/res/layout/altosdroid.xml b/altosdroid/app/src/main/res/layout/altosdroid.xml
new file mode 100644 (file)
index 0000000..df87067
--- /dev/null
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:orientation="vertical" >
+
+       <LinearLayout
+               xmlns:android="http://schemas.android.com/apk/res/android"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:layout_weight="0"
+               android:baselineAligned="true"
+               android:orientation="horizontal" >
+
+               <RelativeLayout
+                       android:id="@+id/callsign_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/callsign_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/callsign_label" />
+
+                       <TextView
+                               android:id="@+id/callsign_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/callsign_label"
+                               android:text=""
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:id="@+id/serial_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/serial_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/serial_label" />
+
+                       <TextView
+                               android:id="@+id/serial_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/serial_label"
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:id="@+id/flight_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/flight_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/flight_label" />
+
+                       <TextView
+                               android:id="@+id/flight_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/flight_label"
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:id="@+id/state_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/state_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/state_label" />
+
+                       <TextView
+                               android:id="@+id/state_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/state_label"
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:id="@+id/rssi_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/rssi_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/rssi_label" />
+
+                       <TextView
+                               android:id="@+id/rssi_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/rssi_label"
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:id="@+id/age_container"
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1" >
+
+                       <TextView
+                               android:id="@+id/age_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:text="@string/age_label" />
+
+                       <TextView
+                               android:id="@+id/age_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@+id/age_label"
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+       </LinearLayout>
+
+       <TabHost
+               xmlns:android="http://schemas.android.com/apk/res/android"
+               android:id="@android:id/tabhost"
+               android:layout_width="fill_parent"
+               android:layout_height="0dp"
+               android:layout_weight="1" >
+
+               <LinearLayout
+                       android:layout_width="match_parent"
+                       android:layout_height="match_parent"
+                       android:orientation="vertical" >
+
+                       <TabWidget
+                               android:id="@android:id/tabs"
+                               android:layout_width="match_parent"
+                               android:layout_height="wrap_content"
+                               android:layout_weight="0"
+                               android:orientation="horizontal" />
+
+                       <FrameLayout
+                               android:id="@android:id/tabcontent"
+                               android:layout_width="0dp"
+                               android:layout_height="0dp"
+                               android:layout_weight="0" />
+
+                       <org.altusmetrum.AltosDroid.AltosViewPager
+                               android:id="@+id/pager"
+                               android:layout_width="match_parent"
+                               android:layout_height="0dp"
+                               android:layout_weight="1" />
+               </LinearLayout>
+       </TabHost>
+
+       <TextView
+               android:id="@+id/version"
+               android:layout_width="fill_parent"
+               android:layout_height="10dip"
+               android:layout_weight="0"
+               android:gravity="bottom|right"
+               android:textSize="7sp"
+               android:typeface="monospace" />
+
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/custom_title.xml b/altosdroid/app/src/main/res/layout/custom_title.xml
new file mode 100644 (file)
index 0000000..57eb6b4
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_vertical"
+  >
+  <TextView android:id="@+id/title_left_text"
+      android:layout_alignParentLeft="true"
+      android:ellipsize="end"
+      android:singleLine="true"
+      style="?android:attr/windowTitleStyle"
+      android:layout_width="wrap_content"
+      android:layout_height="match_parent"
+      android:layout_weight="1"
+    />
+    <TextView android:id="@+id/title_right_text"
+        android:layout_alignParentRight="true"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:textColor="#fff"
+        android:layout_weight="1" 
+    />
+</RelativeLayout>
\ No newline at end of file
diff --git a/altosdroid/app/src/main/res/layout/device_list.xml b/altosdroid/app/src/main/res/layout/device_list.xml
new file mode 100644 (file)
index 0000000..0c103e9
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <Button android:id="@+id/button_scan"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/button_scan"
+    />
+    <TextView android:id="@+id/title_new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/title_other_devices"
+        android:visibility="gone"
+        android:background="#666"
+        android:textColor="#fff"
+        android:paddingLeft="5dp"
+    />
+    <ListView android:id="@+id/new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
+    />
+    <TextView android:id="@+id/title_paired_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/title_paired_devices"
+        android:visibility="gone"
+        android:background="#666"
+        android:textColor="#fff"
+        android:paddingLeft="5dp"
+    />
+    <ListView android:id="@+id/paired_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
+    />
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/device_name.xml b/altosdroid/app/src/main/res/layout/device_name.xml
new file mode 100644 (file)
index 0000000..8fa358c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textSize="18sp"
+    android:padding="5dp"
+/>
\ No newline at end of file
diff --git a/altosdroid/app/src/main/res/layout/frequency.xml b/altosdroid/app/src/main/res/layout/frequency.xml
new file mode 100644 (file)
index 0000000..7adbdc1
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal"
+    >
+  <TextView
+      android:id="@+id/frequency"
+      android:layout_width="wrap_content"
+      android:layout_height="fill_parent"
+      android:padding="10dp"
+      android:layout_weight="1"
+      />
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/idle_mode.xml b/altosdroid/app/src/main/res/layout/idle_mode.xml
new file mode 100644 (file)
index 0000000..6c598c1
--- /dev/null
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+      <TextView android:id="@+id/set_callsign_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/set_callsign_label"
+               />
+      <EditText android:id="@+id/set_callsign"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:hint="@string/set_callsign_label"/>
+      <Button android:id="@+id/connect_idle"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/connect_idle"
+             />
+      <Button android:id="@+id/disconnect_idle"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/disconnect_idle"
+             />
+      <Button android:id="@+id/reboot_idle"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/reboot_idle"
+             />
+      <Button android:id="@+id/igniters_idle"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/igniters_idle"
+             />
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/igniter_status.xml b/altosdroid/app/src/main/res/layout/igniter_status.xml
new file mode 100644 (file)
index 0000000..931ad62
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+  <TextView
+      android:id="@+id/igniter_status"
+      android:layout_width="wrap_content"
+      android:layout_height="fill_parent"
+      android:padding="10dp"
+      android:layout_alignParentRight="true"
+      />
+  <TextView
+      android:id="@+id/igniter_name"
+      android:layout_width="fill_parent"
+      android:layout_height="fill_parent"  
+      android:padding="10dp"
+      android:layout_alignParentLeft="@+id/igniter_status"
+      />
+</RelativeLayout>
diff --git a/altosdroid/app/src/main/res/layout/igniters.xml b/altosdroid/app/src/main/res/layout/igniters.xml
new file mode 100644 (file)
index 0000000..6ce5b24
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+    <ListView android:id="@+id/igniters"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
+       android:choiceMode="singleChoice"
+       />
+
+    <ToggleButton android:id="@+id/igniter_arm"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textOn="@string/igniter_armed"
+            android:textOff="@string/igniter_arm"
+           />
+    
+    <Button android:id="@+id/igniter_fire"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/igniter_fire"
+           />
+    
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/manage_frequencies.xml b/altosdroid/app/src/main/res/layout/manage_frequencies.xml
new file mode 100644 (file)
index 0000000..afb3310
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:id="@+id/set_layout"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:orientation="horizontal"
+       >
+      <EditText
+         android:id="@+id/set_frequency"
+         android:layout_width="wrap_content"
+         android:layout_height="fill_parent"
+         android:padding="10dp"
+         android:layout_weight="1"
+         android:hint="@string/frequency"
+         android:inputType="number|numberDecimal"/>
+      />
+      <TextView
+         android:id="@+id/mhz"
+         android:layout_width="wrap_content"
+         android:layout_height="fill_parent"
+         android:padding="10dp"
+         android:layout_weight="0"
+         android:text="@string/mhz"
+         />
+      <EditText
+         android:id="@+id/set_description"
+         android:layout_width="wrap_content"
+         android:layout_height="fill_parent"  
+         android:padding="10dp"
+         android:layout_weight="2"
+         android:hint="@string/description"
+         />
+    </LinearLayout>
+    <LinearLayout
+       android:orientation="horizontal"
+       android:layout_width="match_parent"
+       android:layout_height="wrap_content"
+       >
+      <Button android:id="@+id/set"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/set"
+             android:layout_weight="1"
+             />
+      
+      <Button android:id="@+id/remove"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/remove"
+             android:layout_weight="1"
+             />
+      
+      <Button android:id="@+id/done"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/done"
+             android:layout_weight="1"
+             />
+    </LinearLayout>
+
+    <ListView android:id="@+id/frequencies"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
+       android:choiceMode="singleChoice"
+       />
+
+    
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/map_preload.xml b/altosdroid/app/src/main/res/layout/map_preload.xml
new file mode 100644 (file)
index 0000000..4e60df2
--- /dev/null
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+  <ScrollView android:layout_width="fill_parent"
+             android:layout_height="wrap_content">
+    <LinearLayout android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                  android:orientation="vertical">
+      <TextView android:id="@+id/preload_site_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_site_label"
+               />
+      <Spinner android:id="@+id/preload_site_list"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_site_label"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_latitude_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_latitude_label"
+               />
+      <EditText android:id="@+id/preload_latitude"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:hint="@string/preload_latitude_label"
+               android:inputType="number|numberSigned|numberDecimal"/>
+      <TextView android:id="@+id/preload_longitude_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_longitude_label"
+               />
+      <EditText android:id="@+id/preload_longitude"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:hint="@string/preload_longitude_label"
+               android:inputType="number|numberSigned|numberDecimal"/>
+      <TextView android:id="@+id/preload_types"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_types"
+               />
+<!--
+      <CheckBox android:id="@+id/preload_hybrid"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_hybrid"
+               />
+      <CheckBox android:id="@+id/preload_satellite"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_satellite"
+               />
+      <CheckBox android:id="@+id/preload_roadmap"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_roadmap"
+               />
+      <CheckBox android:id="@+id/preload_terrain"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_terrain"
+               />
+-->
+      <TextView android:id="@+id/preload_min_zoom_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_min_zoom"
+               />
+      <Spinner android:id="@+id/preload_min_zoom"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_min_zoom"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_max_zoom_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_max_zoom"
+               />
+      <Spinner android:id="@+id/preload_max_zoom"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_max_zoom"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_radius_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_radius"
+               />
+      <Spinner android:id="@+id/preload_radius"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_radius"
+              android:spinnerMode="dropdown"
+              />
+      <Button android:id="@+id/preload_load"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/preload_load"
+             />
+      <ProgressBar android:id="@+id/preload_progress"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  style="@android:style/Widget.ProgressBar.Horizontal"
+                  />
+    </LinearLayout>
+  </ScrollView>
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/map_type.xml b/altosdroid/app/src/main/res/layout/map_type.xml
new file mode 100644 (file)
index 0000000..e5cea22
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+  <Button android:id="@+id/map_type_hybrid"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_hybrid"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_satellite"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_satellite"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_roadmap"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_roadmap"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_terrain"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_terrain"
+          android:onClick="selectType"
+          />
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/setup.xml b/altosdroid/app/src/main/res/layout/setup.xml
new file mode 100644 (file)
index 0000000..eeaf60a
--- /dev/null
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2016 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+  <TableLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:stretchColumns="2,3"
+      android:layout_weight="0"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content">
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      <TextView
+         android:id="@+id/select_rate_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/telemetry_rate"
+         />
+      <Spinner android:id="@+id/select_rate"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/telemetry_rate"
+              android:spinnerMode="dropdown"
+              />
+    </TableRow>
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      <TextView
+         android:id="@+id/set_units_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/set_units"
+         />
+      <Spinner android:id="@+id/set_units"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/set_units"
+              android:spinnerMode="dropdown"
+              />
+    </TableRow>
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      <TextView
+         android:id="@+id/map_type_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/map_type"
+         />
+      <Spinner android:id="@+id/map_type"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/map_type"
+              android:spinnerMode="dropdown"
+              />
+    </TableRow>
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      <TextView
+         android:id="@+id/map_source_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/map_source"
+         />
+      <Spinner android:id="@+id/map_source"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/map_source"
+              android:spinnerMode="dropdown"
+              />
+    </TableRow>
+  </TableLayout>
+  <Button android:id="@+id/preload_maps"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+         android:text="@string/preload_maps"
+         />
+  <Button android:id="@+id/manage_frequencies"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+         android:text="@string/manage_frequencies"
+         />
+  <Button android:id="@+id/done"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+         android:text="@string/done"
+         />
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/tab_flight.xml b/altosdroid/app/src/main/res/layout/tab_flight.xml
new file mode 100644 (file)
index 0000000..36d277d
--- /dev/null
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+  <LinearLayout
+      android:layout_weight="0"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical" >
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/speed_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/speed_label" />
+
+       <TextView
+           android:id="@+id/speed_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/speed_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/height_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/height_label" />
+
+       <TextView
+           android:id="@+id/height_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/height_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/max_speed_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/max_speed_label" />
+
+       <TextView
+           android:id="@+id/max_speed_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/max_speed_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/max_height_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/max_height_label" />
+
+       <TextView
+           android:id="@+id/max_height_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/max_height_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/elevation_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/elevation_label" />
+
+       <TextView
+           android:id="@+id/elevation_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/elevation_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/range_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/range_label" />
+
+       <TextView
+           android:id="@+id/range_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/range_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/bearing_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/bearing_label" />
+
+       <TextView
+           android:id="@+id/bearing_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/bearing_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/compass_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="" />
+
+       <TextView
+           android:id="@+id/compass_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/compass_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/distance_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/gnd_distance_label" />
+
+       <TextView
+           android:id="@+id/distance_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/distance_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <TextView
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+      </TextView>
+
+    </LinearLayout>
+
+    <RelativeLayout
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/latitude_label" />
+
+      <TextView
+         android:id="@+id/lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/longitude_label" />
+
+      <TextView
+         android:id="@+id/lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:id="@+id/apogee_view"
+       android:visibility="gone"
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <ImageView
+         android:id="@+id/apogee_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/apogee_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/apogee_redled"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:paddingRight="5dp"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/apogee_greenled"
+         android:text="@string/apogee_voltage_label" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:id="@+id/main_view"
+       android:visibility="gone"
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <ImageView
+         android:id="@+id/main_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/main_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/main_redled"
+         android:contentDescription="@string/main_voltage_label"
+         android:paddingRight="5dp"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/main_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/main_greenled"
+         android:text="@string/main_voltage_label" />
+
+      <TextView
+         android:id="@+id/main_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/tab_layout.xml b/altosdroid/app/src/main/res/layout/tab_layout.xml
new file mode 100644 (file)
index 0000000..2c21c64
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/customTabLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/tabLabel"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:textSize="13dp"
+        android:textColor="#ffffff"
+       android:gravity="center_horizontal"
+       android:background="#808080"
+        android:layout_centerVertical="true"
+        android:layout_centerHorizontal="true" />
+</RelativeLayout>
diff --git a/altosdroid/app/src/main/res/layout/tab_map.xml b/altosdroid/app/src/main/res/layout/tab_map.xml
new file mode 100644 (file)
index 0000000..25ae3a8
--- /dev/null
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="match_parent"
+       android:layout_height="match_parent"
+       android:orientation="vertical" >
+
+  <FrameLayout
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal"
+      android:layout_weight="1">
+
+    <LinearLayout
+       android:id="@+id/map_online"
+       android:orientation="horizontal"
+       android:layout_width="fill_parent"
+       android:layout_height="fill_parent"
+       android:layout_weight="1">
+    </LinearLayout>
+
+    <org.altusmetrum.AltosDroid.AltosMapOffline
+       android:id="@+id/map_offline"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"/>
+  </FrameLayout>
+  
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/distance_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/distance_label" />
+
+      <TextView
+         android:id="@+id/distance_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/distance_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/bearing_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/bearing_label" />
+
+      <TextView
+         android:id="@+id/bearing_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/bearing_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+  </LinearLayout>
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/target_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/target_latitude_label" />
+
+      <TextView
+         android:id="@+id/target_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/target_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/target_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/target_longitude_label" />
+
+      <TextView
+         android:id="@+id/target_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/target_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/receiver_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/receiver_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/tab_pad.xml b/altosdroid/app/src/main/res/layout/tab_pad.xml
new file mode 100644 (file)
index 0000000..990a6be
--- /dev/null
@@ -0,0 +1,496 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+  <TableLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:stretchColumns="2,3"
+      android:layout_weight="0"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content">
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      
+      <ImageView
+         android:id="@+id/battery_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/battery_voltage_label"
+         android:src="@drawable/grayled"
+         />
+
+      <ImageView
+         android:id="@+id/battery_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/battery_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/battery_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/battery_voltage_label" />
+
+      <TextView
+         android:id="@+id/battery_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall"
+         />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/receiver_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <ImageView
+         android:id="@+id/receiver_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/receiver_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/receiver_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/receiver_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/receiver_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_voltage_label" />
+
+      <TextView
+         android:id="@+id/receiver_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/logging_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/logging_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/logging_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/logging_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/logging_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/logging_label" />
+
+      <TextView
+         android:id="@+id/logging_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/logging_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/gps_locked_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_locked_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/gps_locked_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_locked_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/gps_locked_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/gps_locked_label" />
+
+      <TextView
+         android:id="@+id/gps_locked_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/gps_locked_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/gps_ready_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_ready_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/gps_ready_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_ready_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/gps_ready_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/gps_ready_label" />
+
+      <TextView
+         android:id="@+id/gps_ready_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/gps_ready_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/apogee_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <ImageView
+         android:id="@+id/apogee_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/apogee_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/apogee_voltage_label" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/main_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/main_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/main_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/main_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/main_voltage_label" />
+
+      <TextView
+         android:id="@+id/main_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_a_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_a_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_a_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_a_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_a_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_a_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_a_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_a_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_b_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_b_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_b_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_b_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_b_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_b_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_b_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_b_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_c_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_c_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_c_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_c_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_c_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_c_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_c_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_c_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_d_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_d_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_d_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_d_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_d_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_d_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_d_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_d_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_alt_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_altitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_alt_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+  </TableLayout>
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/layout/tab_recover.xml b/altosdroid/app/src/main/res/layout/tab_recover.xml
new file mode 100644 (file)
index 0000000..7d1e750
--- /dev/null
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_weight="0"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical" >
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <TextView
+         android:id="@+id/bearing_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/bearing_label" />
+
+      <TextView
+         android:id="@+id/bearing_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@+id/bearing_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <TextView
+         android:id="@+id/direction_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/direction_label" />
+
+      <TextView
+         android:id="@+id/direction_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@+id/direction_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/distance_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/distance_label" />
+
+      <TextView
+         android:id="@+id/distance_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@+id/distance_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/target_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/target_latitude_label" />
+
+      <TextView
+         android:id="@+id/target_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/target_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/target_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/target_longitude_label" />
+
+      <TextView
+         android:id="@+id/target_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/target_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/receiver_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/receiver_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_height_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_height_label" />
+
+      <TextView
+         android:id="@+id/max_height_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_height_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_speed_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_speed_label" />
+
+      <TextView
+         android:id="@+id/max_speed_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_speed_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_accel_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_accel_label" />
+
+      <TextView
+         android:id="@+id/max_accel_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_accel_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
diff --git a/altosdroid/app/src/main/res/menu/option_menu.xml b/altosdroid/app/src/main/res/menu/option_menu.xml
new file mode 100644 (file)
index 0000000..53a0de0
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/connect_scan"
+          android:icon="@android:drawable/ic_menu_search"
+          android:title="@string/connect_device" />
+    <item android:id="@+id/disconnect"
+         android:icon="@android:drawable/ic_notification_clear_all"
+         android:title="@string/disconnect_device" />
+    <item android:id="@+id/select_freq"
+          android:icon="@android:drawable/ic_menu_preferences"
+          android:title="@string/select_freq" />
+
+    <item android:id="@+id/select_tracker"
+         android:icon="@android:drawable/ic_menu_view"
+         android:title="@string/select_tracker"/>
+    <item android:id="@+id/delete_track"
+         android:icon="@android:drawable/ic_notification_clear_all"
+         android:title="@string/delete_track"/>
+
+    <item android:id="@+id/setup"
+          android:icon="@android:drawable/ic_menu_preferences"
+          android:title="@string/setup" />
+    <item android:id="@+id/idle_mode"
+          android:icon="@android:drawable/ic_menu_preferences"
+          android:title="@string/idle_mode" />
+
+    <item android:id="@+id/quit"
+          android:icon="@android:drawable/ic_menu_close_clear_cancel"
+          android:title="@string/quit" />
+</menu>
diff --git a/altosdroid/app/src/main/res/values/Colors.xml b/altosdroid/app/src/main/res/values/Colors.xml
new file mode 100644 (file)
index 0000000..8dddae2
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+        Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License along
+        with this program; if not, write to the Free Software Foundation, Inc.,
+        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<resources>
+  <color name="old_color">#ffff4040</color>
+</resources>
diff --git a/altosdroid/app/src/main/res/values/CustomTheme.xml b/altosdroid/app/src/main/res/values/CustomTheme.xml
new file mode 100644 (file)
index 0000000..6c701ea
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+  <style name="CustomTheme" parent="android:Theme.Holo">
+  </style>
+</resources>
diff --git a/altosdroid/app/src/main/res/values/strings.xml b/altosdroid/app/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..6a1d24f
--- /dev/null
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+        Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License along
+        with this program; if not, write to the Free Software Foundation, Inc.,
+        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<resources>
+
+       <string name="app_name">AltosDroid</string>
+
+       <!-- AltosDroid -->
+       <string name="bt_not_enabled">Bluetooth was not enabled.</string>
+       <string name="title_connecting">connecting…</string>
+       <string name="title_connected_to">connected</string>
+       <string name="title_not_connected">not connected</string>
+
+       <!-- Options Menu -->
+       <string name="connect_device">Connect a device</string>
+       <string name="disconnect_device">Disconnect device</string>
+       <string name="quit">Quit</string>
+       <string name="setup">Setup</string>
+       <string name="select_freq">Select radio frequency</string>
+       <string name="select_rate">Select data rate</string>
+       <string name="change_units">Change units</string>
+       <string name="preload_maps">Load Maps</string>
+       <string name="select_tracker">Select Tracker</string>
+       <string name="delete_track">Delete Track</string>
+       <string name="map_type">Map Type</string>
+       <string name="map_source">Toggle Online/Offline maps</string>
+
+       <!-- MapTypeActivity -->
+       <string name="map_type">Map Type</string>
+
+       <!-- DeviceListActivity -->
+       <string name="scanning">scanning for devices…</string>
+       <string name="select_device">select a device to connect</string>
+       <string name="none_paired">No devices have been paired</string>
+       <string name="none_found">No devices found</string>
+       <string name="title_paired_devices">Paired Devices</string>
+       <string name="title_other_devices">Other Available Devices</string>
+       <string name="button_scan">Scan for devices</string>
+
+       <!-- Service -->
+       <string name="telemetry_service_label">AltosDroid Telemetry Service</string>
+       <string name="telemetry_service_started">Telemetry Service Started</string>
+       <string name="telemetry_service_stopped">Telemetry Service Stopped</string>
+
+       <!-- UI fields -->
+       <!-- Header -->
+       <string name="callsign_label">Callsign</string>
+       <string name="serial_label">Serial</string>
+       <string name="flight_label">Flight</string>
+       <string name="state_label">State</string>
+       <string name="rssi_label">RSSI</string>
+       <string name="age_label">Age</string>
+
+       <!-- Tab fields -->
+       <string name="height_label">Height</string>
+       <string name="speed_label">Speed</string>
+       <string name="accel_label">Acceleration</string>
+       <string name="bearing_label">Bearing</string>
+       <string name="direction_label">Direction</string>
+       <string name="elevation_label">Elevation</string>
+       <string name="range_label">Range</string>
+       <string name="distance_label">Distance</string>
+       <string name="gnd_distance_label">Ground Distance</string>
+       <string name="max_height_label">Max Height</string>
+       <string name="max_speed_label">Max Speed</string>
+       <string name="max_accel_label">Max Accel</string>
+       <string name="battery_voltage_label">Battery</string>
+       <string name="receiver_voltage_label">Receiver Battery</string>
+       <string name="apogee_voltage_label">Apogee Igniter</string>
+       <string name="main_voltage_label">Main Igniter</string>
+       <string name="ignite_a_voltage_label">Igniter A</string>
+       <string name="ignite_b_voltage_label">Igniter B</string>
+       <string name="ignite_c_voltage_label">Igniter C</string>
+       <string name="ignite_d_voltage_label">Igniter D</string>
+       <string name="logging_label">Data Logging</string>
+       <string name="gps_locked_label">GPS Locked</string>
+       <string name="gps_ready_label">GPS Ready</string>
+       <string name="latitude_label">Latitude</string>
+       <string name="longitude_label">Longitude</string>
+       <string name="target_latitude_label">Tar Lat</string>
+       <string name="target_longitude_label">Tar Lon</string>
+       <string name="receiver_latitude_label">My Lat</string>
+       <string name="receiver_longitude_label">My Lon</string>
+       <string name="receiver_altitude_label">My Alt</string>
+
+       <!-- Map preload -->
+       <string name="preload_site_label">Known Launch Sites</string>
+       <string name="preload_latitude_label">Latitude</string>
+       <string name="preload_longitude_label">Longitude</string>
+
+       <string name="preload_types">Map Types</string>
+       <string name="preload_hybrid">Hybrid</string>
+       <string name="preload_satellite">Satellite</string>
+       <string name="preload_roadmap">Roadmap</string>
+       <string name="preload_terrain">Terrain</string>
+       <string name="preload_min_zoom">Minimum Zoom</string>
+       <string name="preload_max_zoom">Maximum Zoom</string>
+       <string name="preload_radius">Radius</string>
+       
+       <string name="preload_load">Load Map</string>
+
+       <!-- Idle mode -->
+       <string name="idle_mode">Idle Mode</string>
+       <string name="set_callsign_label">Callsign</string>
+       <string name="connect_idle">Monitor</string>
+       <string name="disconnect_idle">Disconnect</string>
+       <string name="reboot_idle">Reboot</string>
+       <string name="igniters_idle">Fire Igniters</string>
+
+       <!-- igniters -->
+       <string name="igniters">Igniters</string>
+       <string name="igniter_arm">Arm</string>
+       <string name="igniter_armed">Armed</string>
+       <string name="igniter_fire">Fire</string>
+       
+       <!-- setup -->
+       <string name="telemetry_rate">Telemetry Rate</string>
+       <string name="set_units">Units</string>
+       <string name="map_type">Map Type</string>
+       <string name="map_source">Map Source</string>
+       <string name="preload_maps">Preload Maps</string>
+       <string name="manage_frequencies">Manage Frequencies</string>
+       <string name="done">OK</string>
+       
+       <!-- manage frequencies -->
+       <string name="set">Set</string>
+       <string name="mhz">MHz</string>
+       <string name="remove">Remove</string>
+       <string name="done">OK</string>
+       <string name="frequency">Frequency</string>
+       <string name="description">Description</string>
+</resources>
diff --git a/altosdroid/app/src/main/res/xml/device_filter.xml b/altosdroid/app/src/main/res/xml/device_filter.xml
new file mode 100644 (file)
index 0000000..84b09c0
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+  <usb-device vendor-id="65534" />
+  <usb-device vendor-id="1027" />
+</resources>
diff --git a/altosdroid/res/drawable-hdpi/am_status_c.png b/altosdroid/res/drawable-hdpi/am_status_c.png
deleted file mode 100644 (file)
index d439321..0000000
Binary files a/altosdroid/res/drawable-hdpi/am_status_c.png and /dev/null differ
diff --git a/altosdroid/res/drawable-hdpi/am_status_g.png b/altosdroid/res/drawable-hdpi/am_status_g.png
deleted file mode 100644 (file)
index 03f9dd7..0000000
Binary files a/altosdroid/res/drawable-hdpi/am_status_g.png and /dev/null differ
diff --git a/altosdroid/res/drawable-hdpi/app_icon.png b/altosdroid/res/drawable-hdpi/app_icon.png
deleted file mode 100644 (file)
index da2f08a..0000000
Binary files a/altosdroid/res/drawable-hdpi/app_icon.png and /dev/null differ
diff --git a/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png
deleted file mode 100644 (file)
index bc9160d..0000000
Binary files a/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png and /dev/null differ
diff --git a/altosdroid/res/drawable-mdpi/am_status_c.png b/altosdroid/res/drawable-mdpi/am_status_c.png
deleted file mode 100644 (file)
index 30a8d29..0000000
Binary files a/altosdroid/res/drawable-mdpi/am_status_c.png and /dev/null differ
diff --git a/altosdroid/res/drawable-mdpi/am_status_g.png b/altosdroid/res/drawable-mdpi/am_status_g.png
deleted file mode 100644 (file)
index 07f7f07..0000000
Binary files a/altosdroid/res/drawable-mdpi/am_status_g.png and /dev/null differ
diff --git a/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png
deleted file mode 100644 (file)
index 4e427d8..0000000
Binary files a/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png and /dev/null differ
diff --git a/altosdroid/res/drawable/app_icon.png b/altosdroid/res/drawable/app_icon.png
deleted file mode 100644 (file)
index b0a3c9c..0000000
Binary files a/altosdroid/res/drawable/app_icon.png and /dev/null differ
diff --git a/altosdroid/res/drawable/pad.png b/altosdroid/res/drawable/pad.png
deleted file mode 100644 (file)
index b2e65c8..0000000
Binary files a/altosdroid/res/drawable/pad.png and /dev/null differ
diff --git a/altosdroid/res/drawable/rocket.png b/altosdroid/res/drawable/rocket.png
deleted file mode 100644 (file)
index 7e62f6c..0000000
Binary files a/altosdroid/res/drawable/rocket.png and /dev/null differ
diff --git a/altosdroid/res/layout/altosdroid.xml b/altosdroid/res/layout/altosdroid.xml
deleted file mode 100644 (file)
index df87067..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:orientation="vertical" >
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal" >
-
-               <RelativeLayout
-                       android:id="@+id/callsign_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/callsign_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/callsign_label" />
-
-                       <TextView
-                               android:id="@+id/callsign_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/callsign_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:id="@+id/serial_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/serial_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/serial_label" />
-
-                       <TextView
-                               android:id="@+id/serial_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/serial_label"
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:id="@+id/flight_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/flight_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/flight_label" />
-
-                       <TextView
-                               android:id="@+id/flight_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/flight_label"
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:id="@+id/state_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/state_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/state_label" />
-
-                       <TextView
-                               android:id="@+id/state_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/state_label"
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:id="@+id/rssi_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/rssi_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/rssi_label" />
-
-                       <TextView
-                               android:id="@+id/rssi_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/rssi_label"
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:id="@+id/age_container"
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/age_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/age_label" />
-
-                       <TextView
-                               android:id="@+id/age_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_below="@+id/age_label"
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-
-       <TabHost
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:id="@android:id/tabhost"
-               android:layout_width="fill_parent"
-               android:layout_height="0dp"
-               android:layout_weight="1" >
-
-               <LinearLayout
-                       android:layout_width="match_parent"
-                       android:layout_height="match_parent"
-                       android:orientation="vertical" >
-
-                       <TabWidget
-                               android:id="@android:id/tabs"
-                               android:layout_width="match_parent"
-                               android:layout_height="wrap_content"
-                               android:layout_weight="0"
-                               android:orientation="horizontal" />
-
-                       <FrameLayout
-                               android:id="@android:id/tabcontent"
-                               android:layout_width="0dp"
-                               android:layout_height="0dp"
-                               android:layout_weight="0" />
-
-                       <org.altusmetrum.AltosDroid.AltosViewPager
-                               android:id="@+id/pager"
-                               android:layout_width="match_parent"
-                               android:layout_height="0dp"
-                               android:layout_weight="1" />
-               </LinearLayout>
-       </TabHost>
-
-       <TextView
-               android:id="@+id/version"
-               android:layout_width="fill_parent"
-               android:layout_height="10dip"
-               android:layout_weight="0"
-               android:gravity="bottom|right"
-               android:textSize="7sp"
-               android:typeface="monospace" />
-
-</LinearLayout>
diff --git a/altosdroid/res/layout/custom_title.xml b/altosdroid/res/layout/custom_title.xml
deleted file mode 100644 (file)
index 57eb6b4..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center_vertical"
-  >
-  <TextView android:id="@+id/title_left_text"
-      android:layout_alignParentLeft="true"
-      android:ellipsize="end"
-      android:singleLine="true"
-      style="?android:attr/windowTitleStyle"
-      android:layout_width="wrap_content"
-      android:layout_height="match_parent"
-      android:layout_weight="1"
-    />
-    <TextView android:id="@+id/title_right_text"
-        android:layout_alignParentRight="true"
-        android:ellipsize="end"
-        android:singleLine="true"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:textColor="#fff"
-        android:layout_weight="1" 
-    />
-</RelativeLayout>
\ No newline at end of file
diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml
deleted file mode 100644 (file)
index 0c103e9..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-    <Button android:id="@+id/button_scan"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/button_scan"
-    />
-    <TextView android:id="@+id/title_new_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/title_other_devices"
-        android:visibility="gone"
-        android:background="#666"
-        android:textColor="#fff"
-        android:paddingLeft="5dp"
-    />
-    <ListView android:id="@+id/new_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-       android:fadeScrollbars="false"
-       android:scrollbars="vertical"
-    />
-    <TextView android:id="@+id/title_paired_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/title_paired_devices"
-        android:visibility="gone"
-        android:background="#666"
-        android:textColor="#fff"
-        android:paddingLeft="5dp"
-    />
-    <ListView android:id="@+id/paired_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-       android:fadeScrollbars="false"
-       android:scrollbars="vertical"
-    />
-</LinearLayout>
diff --git a/altosdroid/res/layout/device_name.xml b/altosdroid/res/layout/device_name.xml
deleted file mode 100644 (file)
index 8fa358c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:textSize="18sp"
-    android:padding="5dp"
-/>
\ No newline at end of file
diff --git a/altosdroid/res/layout/frequency.xml b/altosdroid/res/layout/frequency.xml
deleted file mode 100644 (file)
index 7adbdc1..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="horizontal"
-    >
-  <TextView
-      android:id="@+id/frequency"
-      android:layout_width="wrap_content"
-      android:layout_height="fill_parent"
-      android:padding="10dp"
-      android:layout_weight="1"
-      />
-</LinearLayout>
diff --git a/altosdroid/res/layout/idle_mode.xml b/altosdroid/res/layout/idle_mode.xml
deleted file mode 100644 (file)
index 6c598c1..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-      <TextView android:id="@+id/set_callsign_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/set_callsign_label"
-               />
-      <EditText android:id="@+id/set_callsign"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:hint="@string/set_callsign_label"/>
-      <Button android:id="@+id/connect_idle"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/connect_idle"
-             />
-      <Button android:id="@+id/disconnect_idle"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/disconnect_idle"
-             />
-      <Button android:id="@+id/reboot_idle"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/reboot_idle"
-             />
-      <Button android:id="@+id/igniters_idle"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/igniters_idle"
-             />
-</LinearLayout>
diff --git a/altosdroid/res/layout/igniter_status.xml b/altosdroid/res/layout/igniter_status.xml
deleted file mode 100644 (file)
index 931ad62..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
-  <TextView
-      android:id="@+id/igniter_status"
-      android:layout_width="wrap_content"
-      android:layout_height="fill_parent"
-      android:padding="10dp"
-      android:layout_alignParentRight="true"
-      />
-  <TextView
-      android:id="@+id/igniter_name"
-      android:layout_width="fill_parent"
-      android:layout_height="fill_parent"  
-      android:padding="10dp"
-      android:layout_alignParentLeft="@+id/igniter_status"
-      />
-</RelativeLayout>
diff --git a/altosdroid/res/layout/igniters.xml b/altosdroid/res/layout/igniters.xml
deleted file mode 100644 (file)
index 6ce5b24..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-
-    <ListView android:id="@+id/igniters"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-       android:fadeScrollbars="false"
-       android:scrollbars="vertical"
-       android:choiceMode="singleChoice"
-       />
-
-    <ToggleButton android:id="@+id/igniter_arm"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textOn="@string/igniter_armed"
-            android:textOff="@string/igniter_arm"
-           />
-    
-    <Button android:id="@+id/igniter_fire"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/igniter_fire"
-           />
-    
-</LinearLayout>
diff --git a/altosdroid/res/layout/manage_frequencies.xml b/altosdroid/res/layout/manage_frequencies.xml
deleted file mode 100644 (file)
index afb3310..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:id="@+id/set_layout"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:orientation="horizontal"
-       >
-      <EditText
-         android:id="@+id/set_frequency"
-         android:layout_width="wrap_content"
-         android:layout_height="fill_parent"
-         android:padding="10dp"
-         android:layout_weight="1"
-         android:hint="@string/frequency"
-         android:inputType="number|numberDecimal"/>
-      />
-      <TextView
-         android:id="@+id/mhz"
-         android:layout_width="wrap_content"
-         android:layout_height="fill_parent"
-         android:padding="10dp"
-         android:layout_weight="0"
-         android:text="@string/mhz"
-         />
-      <EditText
-         android:id="@+id/set_description"
-         android:layout_width="wrap_content"
-         android:layout_height="fill_parent"  
-         android:padding="10dp"
-         android:layout_weight="2"
-         android:hint="@string/description"
-         />
-    </LinearLayout>
-    <LinearLayout
-       android:orientation="horizontal"
-       android:layout_width="match_parent"
-       android:layout_height="wrap_content"
-       >
-      <Button android:id="@+id/set"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:text="@string/set"
-             android:layout_weight="1"
-             />
-      
-      <Button android:id="@+id/remove"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:text="@string/remove"
-             android:layout_weight="1"
-             />
-      
-      <Button android:id="@+id/done"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:text="@string/done"
-             android:layout_weight="1"
-             />
-    </LinearLayout>
-
-    <ListView android:id="@+id/frequencies"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-       android:fadeScrollbars="false"
-       android:scrollbars="vertical"
-       android:choiceMode="singleChoice"
-       />
-
-    
-</LinearLayout>
diff --git a/altosdroid/res/layout/map_preload.xml b/altosdroid/res/layout/map_preload.xml
deleted file mode 100644 (file)
index 4e60df2..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright © 2015 Keith Packard <keithp@keithp.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-  <ScrollView android:layout_width="fill_parent"
-             android:layout_height="wrap_content">
-    <LinearLayout android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                  android:orientation="vertical">
-      <TextView android:id="@+id/preload_site_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_site_label"
-               />
-      <Spinner android:id="@+id/preload_site_list"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/preload_site_label"
-              android:spinnerMode="dropdown"
-              />
-      <TextView android:id="@+id/preload_latitude_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_latitude_label"
-               />
-      <EditText android:id="@+id/preload_latitude"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:hint="@string/preload_latitude_label"
-               android:inputType="number|numberSigned|numberDecimal"/>
-      <TextView android:id="@+id/preload_longitude_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_longitude_label"
-               />
-      <EditText android:id="@+id/preload_longitude"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:hint="@string/preload_longitude_label"
-               android:inputType="number|numberSigned|numberDecimal"/>
-      <TextView android:id="@+id/preload_types"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_types"
-               />
-<!--
-      <CheckBox android:id="@+id/preload_hybrid"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_hybrid"
-               />
-      <CheckBox android:id="@+id/preload_satellite"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_satellite"
-               />
-      <CheckBox android:id="@+id/preload_roadmap"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_roadmap"
-               />
-      <CheckBox android:id="@+id/preload_terrain"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_terrain"
-               />
--->
-      <TextView android:id="@+id/preload_min_zoom_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_min_zoom"
-               />
-      <Spinner android:id="@+id/preload_min_zoom"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/preload_min_zoom"
-              android:spinnerMode="dropdown"
-              />
-      <TextView android:id="@+id/preload_max_zoom_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_max_zoom"
-               />
-      <Spinner android:id="@+id/preload_max_zoom"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/preload_max_zoom"
-              android:spinnerMode="dropdown"
-              />
-      <TextView android:id="@+id/preload_radius_label"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:text="@string/preload_radius"
-               />
-      <Spinner android:id="@+id/preload_radius"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/preload_radius"
-              android:spinnerMode="dropdown"
-              />
-      <Button android:id="@+id/preload_load"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/preload_load"
-             />
-      <ProgressBar android:id="@+id/preload_progress"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  style="@android:style/Widget.ProgressBar.Horizontal"
-                  />
-    </LinearLayout>
-  </ScrollView>
-</LinearLayout>
diff --git a/altosdroid/res/layout/map_type.xml b/altosdroid/res/layout/map_type.xml
deleted file mode 100644 (file)
index e5cea22..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright © 2015 Keith Packard <keithp@keithp.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-  <Button android:id="@+id/map_type_hybrid"
-          android:layout_width="fill_parent"
-          android:layout_height="wrap_content"
-          android:text="@string/preload_hybrid"
-          android:onClick="selectType"
-          />
-  <Button android:id="@+id/map_type_satellite"
-          android:layout_width="fill_parent"
-          android:layout_height="wrap_content"
-          android:text="@string/preload_satellite"
-          android:onClick="selectType"
-          />
-  <Button android:id="@+id/map_type_roadmap"
-          android:layout_width="fill_parent"
-          android:layout_height="wrap_content"
-          android:text="@string/preload_roadmap"
-          android:onClick="selectType"
-          />
-  <Button android:id="@+id/map_type_terrain"
-          android:layout_width="fill_parent"
-          android:layout_height="wrap_content"
-          android:text="@string/preload_terrain"
-          android:onClick="selectType"
-          />
-</LinearLayout>
diff --git a/altosdroid/res/layout/setup.xml b/altosdroid/res/layout/setup.xml
deleted file mode 100644 (file)
index eeaf60a..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
- Copyright © 2016 Keith Packard <keithp@keithp.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-  <TableLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:stretchColumns="2,3"
-      android:layout_weight="0"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content">
-
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-      <TextView
-         android:id="@+id/select_rate_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/telemetry_rate"
-         />
-      <Spinner android:id="@+id/select_rate"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/telemetry_rate"
-              android:spinnerMode="dropdown"
-              />
-    </TableRow>
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-      <TextView
-         android:id="@+id/set_units_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/set_units"
-         />
-      <Spinner android:id="@+id/set_units"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/set_units"
-              android:spinnerMode="dropdown"
-              />
-    </TableRow>
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-      <TextView
-         android:id="@+id/map_type_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/map_type"
-         />
-      <Spinner android:id="@+id/map_type"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/map_type"
-              android:spinnerMode="dropdown"
-              />
-    </TableRow>
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-      <TextView
-         android:id="@+id/map_source_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/map_source"
-         />
-      <Spinner android:id="@+id/map_source"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:prompt="@string/map_source"
-              android:spinnerMode="dropdown"
-              />
-    </TableRow>
-  </TableLayout>
-  <Button android:id="@+id/preload_maps"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-         android:text="@string/preload_maps"
-         />
-  <Button android:id="@+id/manage_frequencies"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-         android:text="@string/manage_frequencies"
-         />
-  <Button android:id="@+id/done"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-         android:text="@string/done"
-         />
-</LinearLayout>
diff --git a/altosdroid/res/layout/tab_flight.xml b/altosdroid/res/layout/tab_flight.xml
deleted file mode 100644 (file)
index 36d277d..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
-     This program is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published by
-     the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-     This program is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License along
-     with this program; if not, write to the Free Software Foundation, Inc.,
-     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-  <LinearLayout
-      android:layout_weight="0"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical" >
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:baselineAligned="true"
-       android:orientation="horizontal" >
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/speed_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/speed_label" />
-
-       <TextView
-           android:id="@+id/speed_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/speed_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/height_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/height_label" />
-
-       <TextView
-           android:id="@+id/height_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/height_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:baselineAligned="true"
-       android:orientation="horizontal" >
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/max_speed_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/max_speed_label" />
-
-       <TextView
-           android:id="@+id/max_speed_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/max_speed_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/max_height_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/max_height_label" />
-
-       <TextView
-           android:id="@+id/max_height_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/max_height_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:baselineAligned="true"
-       android:orientation="horizontal"
-       android:paddingTop="5dp" >
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/elevation_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/elevation_label" />
-
-       <TextView
-           android:id="@+id/elevation_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/elevation_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/range_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/range_label" />
-
-       <TextView
-           android:id="@+id/range_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/range_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:baselineAligned="true"
-       android:orientation="horizontal"
-       android:paddingTop="5dp" >
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/bearing_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/bearing_label" />
-
-       <TextView
-           android:id="@+id/bearing_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/bearing_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/compass_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="" />
-
-       <TextView
-           android:id="@+id/compass_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/compass_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:baselineAligned="true"
-       android:orientation="horizontal"
-       android:paddingTop="5dp" >
-
-      <RelativeLayout
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-
-       <TextView
-           android:id="@+id/distance_label"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:text="@string/gnd_distance_label" />
-
-       <TextView
-           android:id="@+id/distance_value"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_alignParentRight="true"
-           android:layout_below="@id/distance_label"
-           android:text=""
-           android:textAppearance="?android:attr/textAppearanceSmall" />
-      </RelativeLayout>
-
-      <TextView
-         android:layout_width="0dp"
-         android:layout_height="wrap_content"
-         android:layout_weight="1" >
-      </TextView>
-
-    </LinearLayout>
-
-    <RelativeLayout
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/latitude_label" />
-
-      <TextView
-         android:id="@+id/lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/lat_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/longitude_label" />
-
-      <TextView
-         android:id="@+id/lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/lon_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:id="@+id/apogee_view"
-       android:visibility="gone"
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:paddingTop="5dp" >
-
-      <ImageView
-         android:id="@+id/apogee_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/apogee_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/apogee_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/apogee_redled"
-         android:contentDescription="@string/apogee_voltage_label"
-         android:paddingRight="5dp"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/apogee_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/apogee_greenled"
-         android:text="@string/apogee_voltage_label" />
-
-      <TextView
-         android:id="@+id/apogee_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:id="@+id/main_view"
-       android:visibility="gone"
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:paddingTop="5dp" >
-
-      <ImageView
-         android:id="@+id/main_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/main_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/main_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/main_redled"
-         android:contentDescription="@string/main_voltage_label"
-         android:paddingRight="5dp"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/main_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/main_greenled"
-         android:text="@string/main_voltage_label" />
-
-      <TextView
-         android:id="@+id/main_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-  </LinearLayout>
-</LinearLayout>
diff --git a/altosdroid/res/layout/tab_layout.xml b/altosdroid/res/layout/tab_layout.xml
deleted file mode 100644 (file)
index 2c21c64..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/customTabLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <TextView
-        android:id="@+id/tabLabel"
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:textSize="13dp"
-        android:textColor="#ffffff"
-       android:gravity="center_horizontal"
-       android:background="#808080"
-        android:layout_centerVertical="true"
-        android:layout_centerHorizontal="true" />
-</RelativeLayout>
diff --git a/altosdroid/res/layout/tab_map.xml b/altosdroid/res/layout/tab_map.xml
deleted file mode 100644 (file)
index 25ae3a8..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
-     This program is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published by
-     the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-     This program is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License along
-     with this program; if not, write to the Free Software Foundation, Inc.,
-     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="match_parent"
-       android:layout_height="match_parent"
-       android:orientation="vertical" >
-
-  <FrameLayout
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:baselineAligned="true"
-      android:orientation="horizontal"
-      android:layout_weight="1">
-
-    <LinearLayout
-       android:id="@+id/map_online"
-       android:orientation="horizontal"
-       android:layout_width="fill_parent"
-       android:layout_height="fill_parent"
-       android:layout_weight="1">
-    </LinearLayout>
-
-    <org.altusmetrum.AltosDroid.AltosMapOffline
-       android:id="@+id/map_offline"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"/>
-  </FrameLayout>
-  
-  <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:baselineAligned="true"
-      android:orientation="horizontal" >
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/distance_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/distance_label" />
-
-      <TextView
-         android:id="@+id/distance_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/distance_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/bearing_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/bearing_label" />
-
-      <TextView
-         android:id="@+id/bearing_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/bearing_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-  </LinearLayout>
-
-  <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:baselineAligned="true"
-      android:orientation="horizontal" >
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/target_lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/target_latitude_label" />
-
-      <TextView
-         android:id="@+id/target_lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/target_lat_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/target_lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/target_longitude_label" />
-
-      <TextView
-         android:id="@+id/target_lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/target_lon_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-  </LinearLayout>
-
-  <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:baselineAligned="true"
-      android:orientation="horizontal" >
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/receiver_lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/receiver_latitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/receiver_lat_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_width="0dp"
-       android:layout_height="wrap_content"
-       android:layout_weight="1"
-       android:paddingTop="5dp" >
-
-      <TextView
-         android:id="@+id/receiver_lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:paddingRight="4dp"
-         android:text="@string/receiver_longitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_toRightOf="@id/receiver_lon_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-  </LinearLayout>
-</LinearLayout>
diff --git a/altosdroid/res/layout/tab_pad.xml b/altosdroid/res/layout/tab_pad.xml
deleted file mode 100644 (file)
index 990a6be..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
-     This program is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published by
-     the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-     This program is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License along
-     with this program; if not, write to the Free Software Foundation, Inc.,
-     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-  <TableLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:stretchColumns="2,3"
-      android:layout_weight="0"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content">
-
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-      
-      <ImageView
-         android:id="@+id/battery_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/battery_voltage_label"
-         android:src="@drawable/grayled"
-         />
-
-      <ImageView
-         android:id="@+id/battery_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/battery_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/battery_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/battery_voltage_label" />
-
-      <TextView
-         android:id="@+id/battery_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall"
-         />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/receiver_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <ImageView
-         android:id="@+id/receiver_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/receiver_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/receiver_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/receiver_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/receiver_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/receiver_voltage_label" />
-
-      <TextView
-         android:id="@+id/receiver_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/logging_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/logging_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/logging_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/logging_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/logging_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/logging_label" />
-
-      <TextView
-         android:id="@+id/logging_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_below="@id/logging_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/gps_locked_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/gps_locked_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/gps_locked_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/gps_locked_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/gps_locked_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/gps_locked_label" />
-
-      <TextView
-         android:id="@+id/gps_locked_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_below="@id/gps_locked_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/gps_ready_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/gps_ready_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/gps_ready_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/gps_ready_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/gps_ready_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/gps_ready_label" />
-
-      <TextView
-         android:id="@+id/gps_ready_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_below="@id/gps_ready_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/apogee_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <ImageView
-         android:id="@+id/apogee_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/apogee_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/apogee_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/apogee_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/apogee_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/apogee_voltage_label" />
-
-      <TextView
-         android:id="@+id/apogee_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/main_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/main_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/main_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/main_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/main_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/main_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/main_voltage_label" />
-
-      <TextView
-         android:id="@+id/main_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/ignite_a_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/ignite_a_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_a_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/ignite_a_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_a_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/ignite_a_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/ignite_a_voltage_label" />
-
-      <TextView
-         android:id="@+id/ignite_a_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/ignite_b_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/ignite_b_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_b_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/ignite_b_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_b_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/ignite_b_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/ignite_b_voltage_label" />
-
-      <TextView
-         android:id="@+id/ignite_b_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/ignite_c_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/ignite_c_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_c_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/ignite_c_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_c_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/ignite_c_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/ignite_c_voltage_label" />
-
-      <TextView
-         android:id="@+id/ignite_c_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:id="@+id/ignite_d_row"
-       android:visibility="gone"
-       android:layout_gravity="center"
-       android:layout_weight="1"
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <ImageView
-         android:id="@+id/ignite_d_redled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_d_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <ImageView
-         android:id="@+id/ignite_d_greenled"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:contentDescription="@string/ignite_d_voltage_label"
-         android:src="@drawable/grayled" />
-
-      <TextView
-         android:id="@+id/ignite_d_voltage_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/ignite_d_voltage_label" />
-
-      <TextView
-         android:id="@+id/ignite_d_voltage_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/receiver_lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_column="2"
-         android:text="@string/receiver_latitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/receiver_lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_column="2"
-         android:text="@string/receiver_longitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-
-    <TableRow
-       android:padding="2dip"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/receiver_alt_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_column="2"
-         android:text="@string/receiver_altitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_alt_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </TableRow>
-  </TableLayout>
-</LinearLayout>
diff --git a/altosdroid/res/layout/tab_recover.xml b/altosdroid/res/layout/tab_recover.xml
deleted file mode 100644 (file)
index 7d1e750..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
-     This program is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published by
-     the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-     This program is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License along
-     with this program; if not, write to the Free Software Foundation, Inc.,
-     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-  <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_weight="0"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical" >
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <TextView
-         android:id="@+id/bearing_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/bearing_label" />
-
-      <TextView
-         android:id="@+id/bearing_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@+id/bearing_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content" >
-
-      <TextView
-         android:id="@+id/direction_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/direction_label" />
-
-      <TextView
-         android:id="@+id/direction_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@+id/direction_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/distance_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/distance_label" />
-
-      <TextView
-         android:id="@+id/distance_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@+id/distance_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/target_lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/target_latitude_label" />
-
-      <TextView
-         android:id="@+id/target_lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/target_lat_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/target_lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/target_longitude_label" />
-
-      <TextView
-         android:id="@+id/target_lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/target_lon_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/receiver_lat_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/receiver_latitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lat_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/receiver_lat_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/receiver_lon_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/receiver_longitude_label" />
-
-      <TextView
-         android:id="@+id/receiver_lon_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/receiver_lon_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/max_height_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/max_height_label" />
-
-      <TextView
-         android:id="@+id/max_height_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/max_height_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/max_speed_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/max_speed_label" />
-
-      <TextView
-         android:id="@+id/max_speed_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/max_speed_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-
-    <RelativeLayout
-       android:layout_gravity="fill"
-       android:layout_weight="1"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content">
-
-      <TextView
-         android:id="@+id/max_accel_label"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:text="@string/max_accel_label" />
-
-      <TextView
-         android:id="@+id/max_accel_value"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_alignParentRight="true"
-         android:layout_below="@id/max_accel_label"
-         android:text=""
-         android:textAppearance="?android:attr/textAppearanceSmall" />
-    </RelativeLayout>
-  </LinearLayout>
-</LinearLayout>
diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml
deleted file mode 100644 (file)
index 53a0de0..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 
-  Copyright © 2015 Keith Packard <keithp@keithp.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/connect_scan"
-          android:icon="@android:drawable/ic_menu_search"
-          android:title="@string/connect_device" />
-    <item android:id="@+id/disconnect"
-         android:icon="@android:drawable/ic_notification_clear_all"
-         android:title="@string/disconnect_device" />
-    <item android:id="@+id/select_freq"
-          android:icon="@android:drawable/ic_menu_preferences"
-          android:title="@string/select_freq" />
-
-    <item android:id="@+id/select_tracker"
-         android:icon="@android:drawable/ic_menu_view"
-         android:title="@string/select_tracker"/>
-    <item android:id="@+id/delete_track"
-         android:icon="@android:drawable/ic_notification_clear_all"
-         android:title="@string/delete_track"/>
-
-    <item android:id="@+id/setup"
-          android:icon="@android:drawable/ic_menu_preferences"
-          android:title="@string/setup" />
-    <item android:id="@+id/idle_mode"
-          android:icon="@android:drawable/ic_menu_preferences"
-          android:title="@string/idle_mode" />
-
-    <item android:id="@+id/quit"
-          android:icon="@android:drawable/ic_menu_close_clear_cancel"
-          android:title="@string/quit" />
-</menu>
diff --git a/altosdroid/res/values/Colors.xml b/altosdroid/res/values/Colors.xml
deleted file mode 100644 (file)
index 8dddae2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-        Copyright © 2015 Keith Packard <keithp@keithp.com>
-
-        This program is free software; you can redistribute it and/or modify
-        it under the terms of the GNU General Public License as published by
-        the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-        This program is distributed in the hope that it will be useful, but
-        WITHOUT ANY WARRANTY; without even the implied warranty of
-        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-        General Public License for more details.
-
-        You should have received a copy of the GNU General Public License along
-        with this program; if not, write to the Free Software Foundation, Inc.,
-        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<resources>
-  <color name="old_color">#ffff4040</color>
-</resources>
diff --git a/altosdroid/res/values/CustomTheme.xml b/altosdroid/res/values/CustomTheme.xml
deleted file mode 100644 (file)
index 6c701ea..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<resources>
-  <style name="CustomTheme" parent="android:Theme.Holo">
-  </style>
-</resources>
diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml
deleted file mode 100644 (file)
index 6a1d24f..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-        Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
-
-        This program is free software; you can redistribute it and/or modify
-        it under the terms of the GNU General Public License as published by
-        the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-        This program is distributed in the hope that it will be useful, but
-        WITHOUT ANY WARRANTY; without even the implied warranty of
-        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-        General Public License for more details.
-
-        You should have received a copy of the GNU General Public License along
-        with this program; if not, write to the Free Software Foundation, Inc.,
-        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<resources>
-
-       <string name="app_name">AltosDroid</string>
-
-       <!-- AltosDroid -->
-       <string name="bt_not_enabled">Bluetooth was not enabled.</string>
-       <string name="title_connecting">connecting…</string>
-       <string name="title_connected_to">connected</string>
-       <string name="title_not_connected">not connected</string>
-
-       <!-- Options Menu -->
-       <string name="connect_device">Connect a device</string>
-       <string name="disconnect_device">Disconnect device</string>
-       <string name="quit">Quit</string>
-       <string name="setup">Setup</string>
-       <string name="select_freq">Select radio frequency</string>
-       <string name="select_rate">Select data rate</string>
-       <string name="change_units">Change units</string>
-       <string name="preload_maps">Load Maps</string>
-       <string name="select_tracker">Select Tracker</string>
-       <string name="delete_track">Delete Track</string>
-       <string name="map_type">Map Type</string>
-       <string name="map_source">Toggle Online/Offline maps</string>
-
-       <!-- MapTypeActivity -->
-       <string name="map_type">Map Type</string>
-
-       <!-- DeviceListActivity -->
-       <string name="scanning">scanning for devices…</string>
-       <string name="select_device">select a device to connect</string>
-       <string name="none_paired">No devices have been paired</string>
-       <string name="none_found">No devices found</string>
-       <string name="title_paired_devices">Paired Devices</string>
-       <string name="title_other_devices">Other Available Devices</string>
-       <string name="button_scan">Scan for devices</string>
-
-       <!-- Service -->
-       <string name="telemetry_service_label">AltosDroid Telemetry Service</string>
-       <string name="telemetry_service_started">Telemetry Service Started</string>
-       <string name="telemetry_service_stopped">Telemetry Service Stopped</string>
-
-       <!-- UI fields -->
-       <!-- Header -->
-       <string name="callsign_label">Callsign</string>
-       <string name="serial_label">Serial</string>
-       <string name="flight_label">Flight</string>
-       <string name="state_label">State</string>
-       <string name="rssi_label">RSSI</string>
-       <string name="age_label">Age</string>
-
-       <!-- Tab fields -->
-       <string name="height_label">Height</string>
-       <string name="speed_label">Speed</string>
-       <string name="accel_label">Acceleration</string>
-       <string name="bearing_label">Bearing</string>
-       <string name="direction_label">Direction</string>
-       <string name="elevation_label">Elevation</string>
-       <string name="range_label">Range</string>
-       <string name="distance_label">Distance</string>
-       <string name="gnd_distance_label">Ground Distance</string>
-       <string name="max_height_label">Max Height</string>
-       <string name="max_speed_label">Max Speed</string>
-       <string name="max_accel_label">Max Accel</string>
-       <string name="battery_voltage_label">Battery</string>
-       <string name="receiver_voltage_label">Receiver Battery</string>
-       <string name="apogee_voltage_label">Apogee Igniter</string>
-       <string name="main_voltage_label">Main Igniter</string>
-       <string name="ignite_a_voltage_label">Igniter A</string>
-       <string name="ignite_b_voltage_label">Igniter B</string>
-       <string name="ignite_c_voltage_label">Igniter C</string>
-       <string name="ignite_d_voltage_label">Igniter D</string>
-       <string name="logging_label">Data Logging</string>
-       <string name="gps_locked_label">GPS Locked</string>
-       <string name="gps_ready_label">GPS Ready</string>
-       <string name="latitude_label">Latitude</string>
-       <string name="longitude_label">Longitude</string>
-       <string name="target_latitude_label">Tar Lat</string>
-       <string name="target_longitude_label">Tar Lon</string>
-       <string name="receiver_latitude_label">My Lat</string>
-       <string name="receiver_longitude_label">My Lon</string>
-       <string name="receiver_altitude_label">My Alt</string>
-
-       <!-- Map preload -->
-       <string name="preload_site_label">Known Launch Sites</string>
-       <string name="preload_latitude_label">Latitude</string>
-       <string name="preload_longitude_label">Longitude</string>
-
-       <string name="preload_types">Map Types</string>
-       <string name="preload_hybrid">Hybrid</string>
-       <string name="preload_satellite">Satellite</string>
-       <string name="preload_roadmap">Roadmap</string>
-       <string name="preload_terrain">Terrain</string>
-       <string name="preload_min_zoom">Minimum Zoom</string>
-       <string name="preload_max_zoom">Maximum Zoom</string>
-       <string name="preload_radius">Radius</string>
-       
-       <string name="preload_load">Load Map</string>
-
-       <!-- Idle mode -->
-       <string name="idle_mode">Idle Mode</string>
-       <string name="set_callsign_label">Callsign</string>
-       <string name="connect_idle">Monitor</string>
-       <string name="disconnect_idle">Disconnect</string>
-       <string name="reboot_idle">Reboot</string>
-       <string name="igniters_idle">Fire Igniters</string>
-
-       <!-- igniters -->
-       <string name="igniters">Igniters</string>
-       <string name="igniter_arm">Arm</string>
-       <string name="igniter_armed">Armed</string>
-       <string name="igniter_fire">Fire</string>
-       
-       <!-- setup -->
-       <string name="telemetry_rate">Telemetry Rate</string>
-       <string name="set_units">Units</string>
-       <string name="map_type">Map Type</string>
-       <string name="map_source">Map Source</string>
-       <string name="preload_maps">Preload Maps</string>
-       <string name="manage_frequencies">Manage Frequencies</string>
-       <string name="done">OK</string>
-       
-       <!-- manage frequencies -->
-       <string name="set">Set</string>
-       <string name="mhz">MHz</string>
-       <string name="remove">Remove</string>
-       <string name="done">OK</string>
-       <string name="frequency">Frequency</string>
-       <string name="description">Description</string>
-</resources>
diff --git a/altosdroid/res/xml/device_filter.xml b/altosdroid/res/xml/device_filter.xml
deleted file mode 100644 (file)
index 84b09c0..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<resources>
-  <usb-device vendor-id="65534" />
-  <usb-device vendor-id="1027" />
-</resources>
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java
deleted file mode 100644 (file)
index 0d09abd..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.UUID;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothSocket;
-//import android.os.Bundle;
-import android.os.Handler;
-//import android.os.Message;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosBluetooth extends AltosDroidLink {
-
-       private ConnectThread    connect_thread = null;
-
-       private BluetoothDevice  device;
-       private BluetoothSocket  socket;
-       private InputStream      input;
-       private OutputStream     output;
-       private boolean          pause;
-
-       // Constructor
-       public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
-               super(handler);
-               this.device = device;
-               this.handler = handler;
-               this.pause = pause;
-
-               connect_thread = new ConnectThread();
-               connect_thread.start();
-       }
-
-       void connected() {
-               if (closed()) {
-                       AltosDebug.debug("connected after closed");
-                       return;
-               }
-
-               AltosDebug.check_ui("connected\n");
-               try {
-                       synchronized(this) {
-                               if (socket != null) {
-                                       input = socket.getInputStream();
-                                       output = socket.getOutputStream();
-                                       super.connected();
-                               }
-                       }
-               } catch (InterruptedException ie) {
-                       connect_failed();
-               } catch (IOException io) {
-                       connect_failed();
-               }
-       }
-
-       private void connect_failed() {
-               if (closed()) {
-                       AltosDebug.debug("connect_failed after closed");
-                       return;
-               }
-
-               close_device();
-               input = null;
-               output = null;
-               handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
-               AltosDebug.error("ConnectThread: Failed to establish connection");
-       }
-
-       void close_device() {
-               BluetoothSocket tmp_socket;
-
-               synchronized(this) {
-                       tmp_socket = socket;
-                       socket = null;
-               }
-
-               if (tmp_socket != null) {
-                       try {
-                               tmp_socket.close();
-                       } catch (IOException e) {
-                               AltosDebug.error("close_socket failed");
-                       }
-               }
-       }
-
-       public void close() {
-               super.close();
-               input = null;
-               output = null;
-       }
-
-       private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
-
-       private void create_socket(BluetoothDevice  device) {
-
-               BluetoothSocket tmp_socket = null;
-
-               AltosDebug.check_ui("create_socket\n");
-               try {
-                       tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
-               } catch (IOException e) {
-                       e.printStackTrace();
-               }
-               if (socket != null) {
-                       AltosDebug.debug("Socket already allocated %s", socket.toString());
-                       close_device();
-               }
-               synchronized (this) {
-                       socket = tmp_socket;
-               }
-       }
-
-       private class ConnectThread extends Thread {
-
-               public void run() {
-                       AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
-                       setName("ConnectThread");
-
-                       if (pause) {
-                               try {
-                                       Thread.sleep(4000);
-                               } catch (InterruptedException e) {
-                               }
-                       }
-
-                       create_socket(device);
-                       // Always cancel discovery because it will slow down a connection
-                       try {
-                               BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
-                       } catch (Exception e) {
-                               AltosDebug.debug("cancelDiscovery exception %s", e.toString());
-                       }
-
-                       BluetoothSocket local_socket = null;
-
-                       synchronized (AltosBluetooth.this) {
-                               if (!closed())
-                                       local_socket = socket;
-                       }
-
-                       if (local_socket != null) {
-                               try {
-                                       // Make a connection to the BluetoothSocket
-                                       // This is a blocking call and will only return on a
-                                       // successful connection or an exception
-                                       local_socket.connect();
-                               } catch (Exception e) {
-                                       AltosDebug.debug("Connect exception %s", e.toString());
-                                       try {
-                                               local_socket.close();
-                                       } catch (Exception ce) {
-                                               AltosDebug.debug("Close exception %s", ce.toString());
-                                       }
-                                       local_socket = null;
-                               }
-                       }
-
-                       if (local_socket != null) {
-                               connected();
-                       } else {
-                               connect_failed();
-                       }
-
-                       AltosDebug.debug("ConnectThread: completed");
-               }
-       }
-
-       private synchronized void wait_connected() throws InterruptedException, IOException {
-               AltosDebug.check_ui("wait_connected\n");
-               if (input == null && socket != null) {
-                       AltosDebug.debug("wait_connected...");
-                       wait();
-                       AltosDebug.debug("wait_connected done");
-               }
-               if (socket == null)
-                       throw new IOException();
-       }
-
-       int write(byte[] buffer, int len) {
-               if (output == null)
-                       return -1;
-               try {
-                       output.write(buffer, 0, len);
-               } catch (IOException ie) {
-                       return -1;
-               }
-               return len;
-       }
-
-       int read(byte[] buffer, int len) {
-               if (input == null)
-                       return -1;
-               try {
-                       return input.read(buffer, 0, len);
-               } catch (IOException ie) {
-                       return -1;
-               }
-       }
-
-       // Stubs of required methods when extending AltosLink
-       public boolean can_cancel_reply()   { return false; }
-       public boolean show_reply_timeout() { return true; }
-       public void hide_reply_timeout()    { }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java
deleted file mode 100644 (file)
index 2ae0652..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-package org.altusmetrum.AltosDroid;
-
-import java.util.Arrays;
-import java.io.*;
-import java.lang.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.graphics.*;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.view.*;
-import android.widget.*;
-import android.location.Location;
-import android.content.*;
-import android.util.Log;
-import android.os.*;
-import android.content.pm.*;
-
-public class AltosDebug {
-       // Debugging
-       static final String TAG = "AltosDroid";
-
-       static boolean  D = true;
-
-       static void init(Context context) {
-               ApplicationInfo app_info = context.getApplicationInfo();
-
-               if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                       Log.d(TAG, "Enable debugging\n");
-                       D = true;
-               } else {
-                       Log.d(TAG, "Disable debugging\n");
-                       D = false;
-               }
-       }
-
-
-       static void info(String format, Object ... arguments) {
-               Log.i(TAG, String.format(format, arguments));
-       }
-
-       static void debug(String format, Object ... arguments) {
-               if (D)
-                       Log.d(TAG, String.format(format, arguments));
-       }
-
-       static void error(String format, Object ... arguments) {
-               Log.e(TAG, String.format(format, arguments));
-       }
-
-       static void check_ui(String format, Object ... arguments) {
-               if (Looper.myLooper() == Looper.getMainLooper()) {
-                       Log.e(TAG, String.format("ON UI THREAD " + format, arguments));
-                       for (StackTraceElement el : Thread.currentThread().getStackTrace())
-                               Log.e(TAG, "\t" + el.toString() + "\n");
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
deleted file mode 100644 (file)
index 1bcb67e..0000000
+++ /dev/null
@@ -1,1224 +0,0 @@
-/*
- * Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.lang.ref.WeakReference;
-import java.text.*;
-import java.util.*;
-import java.io.*;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.Intent;
-import android.content.Context;
-import android.content.ComponentName;
-import android.content.ServiceConnection;
-import android.content.DialogInterface;
-import android.os.IBinder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.content.res.Resources;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.util.DisplayMetrics;
-import android.view.*;
-import android.widget.*;
-import android.app.AlertDialog;
-import android.location.Location;
-import android.location.LocationManager;
-import android.location.LocationListener;
-import android.hardware.usb.*;
-import android.graphics.*;
-import android.graphics.drawable.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-class SavedState {
-       long    received_time;
-       int     state;
-       boolean locked;
-       String  callsign;
-       int     serial;
-       int     flight;
-       int     rssi;
-
-       SavedState(AltosState state) {
-               received_time = state.received_time;
-               this.state = state.state();
-               if (state.gps != null)
-                       locked = state.gps.locked;
-               else
-                       locked = false;
-               callsign = state.cal_data().callsign;
-               serial = state.cal_data().serial;
-               flight = state.cal_data().flight;
-               rssi = state.rssi;
-       }
-}
-
-class Tracker implements CharSequence, Comparable {
-       int     serial;
-       String  call;
-       double  frequency;
-
-       String  display;
-
-       public Tracker(int serial, String call, double frequency) {
-               if (call == null)
-                       call = "none";
-
-               this.serial = serial;
-               this.call = call;
-               this.frequency = frequency;
-               if (frequency == 0.0)
-                       display = "Auto";
-               else if (frequency == AltosLib.MISSING) {
-                       display = String.format("%-8.8s  %6d", call, serial);
-               } else {
-                       display = String.format("%-8.8s %7.3f %6d", call, frequency, serial);
-               }
-       }
-
-       public Tracker(AltosState s) {
-               this(s == null ? 0 : s.cal_data().serial,
-                    s == null ? null : s.cal_data().callsign,
-                    s == null ? 0.0 : s.frequency);
-       }
-
-       /* CharSequence */
-       public char charAt(int index) {
-               return display.charAt(index);
-       }
-
-       public int length() {
-               return display.length();
-       }
-
-       public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException {
-               return display.subSequence(start, end);
-       }
-
-       public String toString() {
-               return display.toString();
-       }
-
-       /* Comparable */
-       public int compareTo (Object other) {
-               Tracker o = (Tracker) other;
-               if (frequency == 0.0) {
-                       if (o.frequency == 0.0)
-                               return 0;
-                       return -1;
-               }
-               if (o.frequency == 0.0)
-                       return 1;
-
-               int     a = serial - o.serial;
-               int     b = call.compareTo(o.call);
-               int     c = (int) Math.signum(frequency - o.frequency);
-
-               if (b != 0)
-                       return b;
-               if (c != 0)
-                       return c;
-               return a;
-       }
-}
-
-public class AltosDroid extends FragmentActivity implements AltosUnitsListener, LocationListener {
-
-       // Actions sent to the telemetry server at startup time
-
-       public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH";
-       public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB";
-
-       // Message types received by our Handler
-
-       public static final int MSG_STATE           = 1;
-       public static final int MSG_UPDATE_AGE      = 2;
-       public static final int MSG_IDLE_MODE       = 3;
-       public static final int MSG_IGNITER_STATUS  = 4;
-
-       // Intent request codes
-       public static final int REQUEST_CONNECT_DEVICE = 1;
-       public static final int REQUEST_ENABLE_BT      = 2;
-       public static final int REQUEST_PRELOAD_MAPS   = 3;
-       public static final int REQUEST_IDLE_MODE      = 5;
-       public static final int REQUEST_IGNITERS       = 6;
-       public static final int REQUEST_SETUP          = 7;
-
-       public static final String EXTRA_IDLE_MODE = "idle_mode";
-       public static final String EXTRA_IDLE_RESULT = "idle_result";
-       public static final String EXTRA_TELEMETRY_SERVICE = "telemetry_service";
-
-       // Setup result bits
-       public static final int SETUP_BAUD = 1;
-       public static final int SETUP_UNITS = 2;
-       public static final int SETUP_MAP_SOURCE = 4;
-       public static final int SETUP_MAP_TYPE = 8;
-
-       public static FragmentManager   fm;
-
-       private BluetoothAdapter mBluetoothAdapter = null;
-
-       // Flight state values
-       private TextView mCallsignView;
-       private TextView mRSSIView;
-       private TextView mSerialView;
-       private TextView mFlightView;
-       private RelativeLayout mStateLayout;
-       private TextView mStateView;
-       private TextView mAgeView;
-       private boolean  mAgeViewOld;
-       private int mAgeNewColor;
-       private int mAgeOldColor;
-
-       public static final String      tab_pad_name = "pad";
-       public static final String      tab_flight_name = "flight";
-       public static final String      tab_recover_name = "recover";
-       public static final String      tab_map_name = "map";
-
-       // field to display the version at the bottom of the screen
-       private TextView mVersion;
-
-       private boolean idle_mode = false;
-
-       public Location location = null;
-
-       private AltosState      state;
-       private SavedState      saved_state;
-
-       // Tabs
-       TabHost         mTabHost;
-       AltosViewPager  mViewPager;
-       TabsAdapter     mTabsAdapter;
-       ArrayList<AltosDroidTab> mTabs = new ArrayList<AltosDroidTab>();
-       int             tabHeight;
-
-       // Timer and Saved flight state for Age calculation
-       private Timer timer;
-
-       TelemetryState  telemetry_state;
-       Tracker[]       trackers;
-
-
-       UsbDevice       pending_usb_device;
-       boolean         start_with_usb;
-
-       // Service
-       private boolean mIsBound   = false;
-       private Messenger mService = null;
-       final Messenger mMessenger = new Messenger(new IncomingHandler(this));
-
-       // Text to Speech
-       private AltosVoice mAltosVoice = null;
-
-       // The Handler that gets information back from the Telemetry Service
-       static class IncomingHandler extends Handler {
-               private final WeakReference<AltosDroid> mAltosDroid;
-               IncomingHandler(AltosDroid ad) { mAltosDroid = new WeakReference<AltosDroid>(ad); }
-
-               @Override
-               public void handleMessage(Message msg) {
-                       AltosDroid ad = mAltosDroid.get();
-
-                       switch (msg.what) {
-                       case MSG_STATE:
-                               if (msg.obj == null) {
-                                       AltosDebug.debug("telemetry_state null!");
-                                       return;
-                               }
-                               ad.update_state((TelemetryState) msg.obj);
-                               break;
-                       case MSG_UPDATE_AGE:
-                               ad.update_age();
-                               break;
-                       case MSG_IDLE_MODE:
-                               ad.idle_mode = (Boolean) msg.obj;
-                               ad.update_state(null);
-                               break;
-                       }
-               }
-       };
-
-
-       private ServiceConnection mConnection = new ServiceConnection() {
-               public void onServiceConnected(ComponentName className, IBinder service) {
-                       mService = new Messenger(service);
-                       try {
-                               Message msg = Message.obtain(null, TelemetryService.MSG_REGISTER_CLIENT);
-                               msg.replyTo = mMessenger;
-                               mService.send(msg);
-                       } catch (RemoteException e) {
-                               // In this case the service has crashed before we could even do anything with it
-                       }
-                       if (pending_usb_device != null) {
-                               try {
-                                       mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device));
-                                       pending_usb_device = null;
-                               } catch (RemoteException e) {
-                               }
-                       }
-               }
-
-               public void onServiceDisconnected(ComponentName className) {
-                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
-                       mService = null;
-               }
-       };
-
-       void doBindService() {
-               bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE);
-               mIsBound = true;
-       }
-
-       void doUnbindService() {
-               if (mIsBound) {
-                       // If we have received the service, and hence registered with it, then now is the time to unregister.
-                       if (mService != null) {
-                               try {
-                                       Message msg = Message.obtain(null, TelemetryService.MSG_UNREGISTER_CLIENT);
-                                       msg.replyTo = mMessenger;
-                                       mService.send(msg);
-                               } catch (RemoteException e) {
-                                       // There is nothing special we need to do if the service has crashed.
-                               }
-                       }
-                       // Detach our existing connection.
-                       unbindService(mConnection);
-                       mIsBound = false;
-               }
-       }
-
-       public void registerTab(AltosDroidTab mTab) {
-               mTabs.add(mTab);
-       }
-
-       public void unregisterTab(AltosDroidTab mTab) {
-               mTabs.remove(mTab);
-       }
-
-       public void units_changed(boolean imperial_units) {
-               for (AltosDroidTab mTab : mTabs)
-                       mTab.units_changed(imperial_units);
-       }
-
-       void update_title(TelemetryState telemetry_state) {
-               switch (telemetry_state.connect) {
-               case TelemetryState.CONNECT_CONNECTED:
-                       if (telemetry_state.config != null) {
-                               String str = String.format("S/N %d %6.3f MHz%s", telemetry_state.config.serial,
-                                                          telemetry_state.frequency, idle_mode ? " (idle)" : "");
-                               if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
-                                       str = str.concat(String.format(" %d bps",
-                                                                      AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
-                               setTitle(str);
-                       } else {
-                               setTitle(R.string.title_connected_to);
-                       }
-                       break;
-               case TelemetryState.CONNECT_CONNECTING:
-                       if (telemetry_state.address != null)
-                               setTitle(String.format("Connecting to %s...", telemetry_state.address.name));
-                       else
-                               setTitle("Connecting to something...");
-                       break;
-               case TelemetryState.CONNECT_DISCONNECTED:
-               case TelemetryState.CONNECT_NONE:
-                       setTitle(R.string.title_not_connected);
-                       break;
-               }
-       }
-
-       void start_timer() {
-               if (timer == null) {
-                       timer = new Timer();
-                       timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 1000L);
-               }
-       }
-
-       void stop_timer() {
-               if (timer != null) {
-                       timer.cancel();
-                       timer.purge();
-                       timer = null;
-               }
-       }
-
-       int     selected_serial = 0;
-       int     current_serial;
-       long    switch_time;
-
-       void set_switch_time() {
-               switch_time = System.currentTimeMillis();
-               selected_serial = 0;
-       }
-
-       boolean registered_units_listener;
-
-       void update_state(TelemetryState new_telemetry_state) {
-
-               if (new_telemetry_state != null)
-                       telemetry_state = new_telemetry_state;
-
-               if (selected_serial != 0)
-                       current_serial = selected_serial;
-
-               if (current_serial == 0)
-                       current_serial = telemetry_state.latest_serial;
-
-               if (!registered_units_listener) {
-                       registered_units_listener = true;
-                       AltosPreferences.register_units_listener(this);
-               }
-
-               int     num_trackers = 0;
-               for (AltosState s : telemetry_state.states.values()) {
-                       num_trackers++;
-               }
-
-               trackers = new Tracker[num_trackers + 1];
-
-               int n = 0;
-               trackers[n++] = new Tracker(0, "auto", 0.0);
-
-               for (AltosState s : telemetry_state.states.values())
-                       trackers[n++] = new Tracker(s);
-
-               Arrays.sort(trackers);
-
-               update_title(telemetry_state);
-
-               AltosState      state = null;
-               boolean         aged = true;
-
-               if (telemetry_state.states.containsKey(current_serial)) {
-                       state = telemetry_state.states.get(current_serial);
-                       int age = state_age(state.received_time);
-                       if (age < 20)
-                               aged = false;
-                       if (current_serial == selected_serial)
-                               aged = false;
-                       else if (switch_time != 0 && (switch_time - state.received_time) > 0)
-                               aged = true;
-               }
-
-               if (aged) {
-                       AltosState      newest_state = null;
-                       int             newest_age = 0;
-
-                       for (int serial : telemetry_state.states.keySet()) {
-                               AltosState      existing = telemetry_state.states.get(serial);
-                               int             existing_age = state_age(existing.received_time);
-
-                               if (newest_state == null || existing_age < newest_age) {
-                                       newest_state = existing;
-                                       newest_age = existing_age;
-                               }
-                       }
-
-                       if (newest_state != null)
-                               state = newest_state;
-               }
-
-               update_ui(telemetry_state, state, telemetry_state.quiet);
-
-               start_timer();
-       }
-
-       boolean same_string(String a, String b) {
-               if (a == null) {
-                       if (b == null)
-                               return true;
-                       return false;
-               } else {
-                       if (b == null)
-                               return false;
-                       return a.equals(b);
-               }
-       }
-
-
-       private int blend_component(int a, int b, double r, int shift, int mask) {
-               return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
-       }
-       private int blend_color(int a, int b, double r) {
-               return (blend_component(a, b, r, 0, 0xff) |
-                       blend_component(a, b, r, 8, 0xff) |
-                       blend_component(a, b, r, 16, 0xff) |
-                       blend_component(a, b, r, 24, 0xff));
-       }
-
-       int state_age(long received_time) {
-               return (int) ((System.currentTimeMillis() - received_time + 500) / 1000);
-       }
-
-       void set_screen_on(int age) {
-               if (age < 60)
-                       getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-               else
-                       getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-       }
-
-       void update_age() {
-               if (saved_state != null) {
-                       int age = state_age(saved_state.received_time);
-
-                       double age_scale = age / 100.0;
-
-                       if (age_scale > 1.0)
-                               age_scale = 1.0;
-
-                       mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
-
-                       set_screen_on(age);
-
-                       String  text;
-                       if (age < 60)
-                               text = String.format("%ds", age);
-                       else if (age < 60 * 60)
-                               text = String.format("%dm", age / 60);
-                       else if (age < 60 * 60 * 24)
-                               text = String.format("%dh", age / (60 * 60));
-                       else
-                               text = String.format("%dd", age / (24 * 60 * 60));
-                       mAgeView.setText(text);
-               }
-       }
-
-       void update_ui(TelemetryState telem_state, AltosState state, boolean quiet) {
-
-               this.state = state;
-
-               int prev_state = AltosLib.ao_flight_invalid;
-
-               AltosGreatCircle from_receiver = null;
-
-               if (saved_state != null)
-                       prev_state = saved_state.state;
-
-               if (state != null) {
-                       set_screen_on(state_age(state.received_time));
-
-                       if (state.state() == AltosLib.ao_flight_stateless) {
-                               boolean prev_locked = false;
-                               boolean locked = false;
-
-                               if(state.gps != null)
-                                       locked = state.gps.locked;
-                               if (saved_state != null)
-                                       prev_locked = saved_state.locked;
-                               if (prev_locked != locked) {
-                                       String currentTab = mTabHost.getCurrentTabTag();
-                                       if (locked) {
-                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
-                                       } else {
-                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_pad_name);
-                                       }
-                               }
-                       } else {
-                               if (prev_state != state.state()) {
-                                       String currentTab = mTabHost.getCurrentTabTag();
-                                       switch (state.state()) {
-                                       case AltosLib.ao_flight_boost:
-                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
-                                               break;
-                                       case AltosLib.ao_flight_landed:
-                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_recover_name);
-                                               break;
-                                       case AltosLib.ao_flight_stateless:
-                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (location != null && state.gps != null && state.gps.locked) {
-                               double altitude = 0;
-                               if (location.hasAltitude())
-                                       altitude = location.getAltitude();
-                               from_receiver = new AltosGreatCircle(location.getLatitude(),
-                                                                    location.getLongitude(),
-                                                                    altitude,
-                                                                    state.gps.lat,
-                                                                    state.gps.lon,
-                                                                    state.gps.alt);
-                       }
-
-                       if (saved_state == null || !same_string(saved_state.callsign, state.cal_data().callsign)) {
-                               mCallsignView.setText(state.cal_data().callsign);
-                       }
-                       if (saved_state == null || state.cal_data().serial != saved_state.serial) {
-                               if (state.cal_data().serial == AltosLib.MISSING)
-                                       mSerialView.setText("");
-                               else
-                                       mSerialView.setText(String.format("%d", state.cal_data().serial));
-                       }
-                       if (saved_state == null || state.cal_data().flight != saved_state.flight) {
-                               if (state.cal_data().flight == AltosLib.MISSING)
-                                       mFlightView.setText("");
-                               else
-                                       mFlightView.setText(String.format("%d", state.cal_data().flight));
-                       }
-                       if (saved_state == null || state.state() != saved_state.state) {
-                               if (state.state() == AltosLib.ao_flight_stateless) {
-                                       mStateLayout.setVisibility(View.GONE);
-                               } else {
-                                       mStateView.setText(state.state_name());
-                                       mStateLayout.setVisibility(View.VISIBLE);
-                               }
-                       }
-                       if (saved_state == null || state.rssi != saved_state.rssi) {
-                               if (state.rssi == AltosLib.MISSING)
-                                       mRSSIView.setText("");
-                               else
-                                       mRSSIView.setText(String.format("%d", state.rssi));
-                       }
-                       saved_state = new SavedState(state);
-               }
-
-               for (AltosDroidTab mTab : mTabs)
-                       mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
-
-               AltosDebug.debug("quiet %b\n", quiet);
-               if (mAltosVoice != null)
-                       mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem(), quiet);
-
-       }
-
-       private void onTimerTick() {
-               try {
-                       mMessenger.send(Message.obtain(null, MSG_UPDATE_AGE));
-               } catch (RemoteException e) {
-               }
-       }
-
-       static String pos(double p, String pos, String neg) {
-               String  h = pos;
-               if (p == AltosLib.MISSING)
-                       return "";
-               if (p < 0) {
-                       h = neg;
-                       p = -p;
-               }
-               int deg = (int) Math.floor(p);
-               double min = (p - Math.floor(p)) * 60.0;
-               return String.format("%d°%9.4f\" %s", deg, min, h);
-       }
-
-       static String number(String format, double value) {
-               if (value == AltosLib.MISSING)
-                       return "";
-               return String.format(format, value);
-       }
-
-       static String integer(String format, int value) {
-               if (value == AltosLib.MISSING)
-                       return "";
-               return String.format(format, value);
-       }
-
-       private View create_tab_view(String label) {
-               LayoutInflater inflater = (LayoutInflater) this.getLayoutInflater();
-               View tab_view = inflater.inflate(R.layout.tab_layout, null);
-               TextView text_view = (TextView) tab_view.findViewById (R.id.tabLabel);
-               text_view.setText(label);
-               return tab_view;
-       }
-
-       @Override
-       public void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-               AltosDebug.init(this);
-               AltosDebug.debug("+++ ON CREATE +++");
-
-               // Initialise preferences
-               AltosDroidPreferences.init(this);
-
-               fm = getSupportFragmentManager();
-
-               // Set up the window layout
-               setContentView(R.layout.altosdroid);
-
-               // Create the Tabs and ViewPager
-               mTabHost = (TabHost)findViewById(android.R.id.tabhost);
-               mTabHost.setup();
-
-               mViewPager = (AltosViewPager)findViewById(R.id.pager);
-               mViewPager.setOffscreenPageLimit(4);
-
-               mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
-
-               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null);
-
-               // Display the Version
-               mVersion = (TextView) findViewById(R.id.version);
-               mVersion.setText("Version: " + BuildInfo.version +
-                                " Built: " + BuildInfo.builddate + " " + BuildInfo.buildtime + " " + BuildInfo.buildtz +
-                                " (" + BuildInfo.branch + "-" + BuildInfo.commitnum + "-" + BuildInfo.commithash + ")");
-
-               mCallsignView  = (TextView) findViewById(R.id.callsign_value);
-               mRSSIView      = (TextView) findViewById(R.id.rssi_value);
-               mSerialView    = (TextView) findViewById(R.id.serial_value);
-               mFlightView    = (TextView) findViewById(R.id.flight_value);
-               mStateLayout   = (RelativeLayout) findViewById(R.id.state_container);
-               mStateView     = (TextView) findViewById(R.id.state_value);
-               mAgeView       = (TextView) findViewById(R.id.age_value);
-               mAgeNewColor   = mAgeView.getTextColors().getDefaultColor();
-               mAgeOldColor   = getResources().getColor(R.color.old_color);
-       }
-
-       private void ensureBluetooth() {
-               // Get local Bluetooth adapter
-               mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-
-               /* if there is a BT adapter and it isn't turned on, then turn it on */
-               if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
-                       Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-                       startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
-               }
-       }
-
-       private boolean check_usb() {
-               UsbDevice       device = AltosUsb.find_device(this, AltosLib.product_basestation);
-
-               if (device != null) {
-                       Intent          i = new Intent(this, AltosDroid.class);
-                       PendingIntent   pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0);
-
-                       if (AltosUsb.request_permission(this, device, pi)) {
-                               connectUsb(device);
-                       }
-                       start_with_usb = true;
-                       return true;
-               }
-
-               start_with_usb = false;
-
-               return false;
-       }
-
-       private void noticeIntent(Intent intent) {
-
-               /* Ok, this is pretty convenient.
-                *
-                * When a USB device is plugged in, and our 'hotplug'
-                * intent registration fires, we get an Intent with
-                * EXTRA_DEVICE set.
-                *
-                * When we start up and see a usb device and request
-                * permission to access it, that queues a
-                * PendingIntent, which has the EXTRA_DEVICE added in,
-                * along with the EXTRA_PERMISSION_GRANTED field as
-                * well.
-                *
-                * So, in both cases, we get the device name using the
-                * same call. We check to see if access was granted,
-                * in which case we ignore the device field and do our
-                * usual startup thing.
-                */
-
-               UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
-               boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
-
-               AltosDebug.debug("intent %s device %s granted %s", intent, device, granted);
-
-               if (!granted)
-                       device = null;
-
-               if (device != null) {
-                       AltosDebug.debug("intent has usb device " + device.toString());
-                       connectUsb(device);
-               } else {
-
-                       /* 'granted' is only false if this intent came
-                        * from the request_permission call and
-                        * permission was denied. In which case, we
-                        * don't want to loop forever...
-                        */
-                       if (granted) {
-                               AltosDebug.debug("check for a USB device at startup");
-                               if (check_usb())
-                                       return;
-                       }
-                       AltosDebug.debug("Starting by looking for bluetooth devices");
-                       ensureBluetooth();
-               }
-       }
-
-       @Override
-       public void onStart() {
-               super.onStart();
-               AltosDebug.debug("++ ON START ++");
-
-               set_switch_time();
-
-               noticeIntent(getIntent());
-
-               // Start Telemetry Service
-               String  action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH;
-
-               startService(new Intent(action, null, AltosDroid.this, TelemetryService.class));
-
-               doBindService();
-
-               if (mAltosVoice == null)
-                       mAltosVoice = new AltosVoice(this);
-
-       }
-
-       @Override
-       public void onNewIntent(Intent intent) {
-               super.onNewIntent(intent);
-               AltosDebug.debug("onNewIntent");
-               noticeIntent(intent);
-       }
-
-       @Override
-       public void onResume() {
-               super.onResume();
-               AltosDebug.debug("+ ON RESUME +");
-
-               // Listen for GPS and Network position updates
-               LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
-               locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-
-               location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
-
-               if (location != null)
-                       AltosDebug.debug("Resume, location is %f,%f\n",
-                                        location.getLatitude(),
-                                        location.getLongitude());
-
-               update_ui(telemetry_state, state, true);
-       }
-
-       @Override
-       public void onPause() {
-               super.onPause();
-               AltosDebug.debug("- ON PAUSE -");
-               // Stop listening for location updates
-               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
-       }
-
-       @Override
-       public void onStop() {
-               super.onStop();
-               AltosDebug.debug("-- ON STOP --");
-       }
-
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               AltosDebug.debug("--- ON DESTROY ---");
-
-               doUnbindService();
-               if (mAltosVoice != null) {
-                       mAltosVoice.stop();
-                       mAltosVoice = null;
-               }
-               stop_timer();
-       }
-
-       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-               AltosDebug.debug("onActivityResult " + resultCode);
-               switch (requestCode) {
-               case REQUEST_CONNECT_DEVICE:
-                       // When DeviceListActivity returns with a device to connect to
-                       if (resultCode == Activity.RESULT_OK) {
-                               connectDevice(data);
-                       }
-                       break;
-               case REQUEST_ENABLE_BT:
-                       // When the request to enable Bluetooth returns
-                       if (resultCode == Activity.RESULT_OK) {
-                               // Bluetooth is now enabled, so set up a chat session
-                               //setupChat();
-                               AltosDebug.debug("BT enabled");
-                               bluetoothEnabled(data);
-                       } else {
-                               // User did not enable Bluetooth or an error occured
-                               AltosDebug.debug("BT not enabled");
-                       }
-                       break;
-               case REQUEST_IDLE_MODE:
-                       if (resultCode == Activity.RESULT_OK)
-                               idle_mode(data);
-                       break;
-               case REQUEST_IGNITERS:
-                       break;
-               case REQUEST_SETUP:
-                       if (resultCode == Activity.RESULT_OK)
-                               note_setup_changes(data);
-                       break;
-               }
-       }
-
-       private void note_setup_changes(Intent data) {
-               int changes = data.getIntExtra(SetupActivity.EXTRA_SETUP_CHANGES, 0);
-
-               if ((changes & SETUP_BAUD) != 0) {
-                       try {
-                               mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD,
-                                                            AltosPreferences.telemetry_rate(1)));
-                       } catch (RemoteException re) {
-                       }
-               }
-               if ((changes & SETUP_UNITS) != 0) {
-                       /* nothing to do here */
-               }
-               if ((changes & SETUP_MAP_SOURCE) != 0) {
-                       /* nothing to do here */
-               }
-               if ((changes & SETUP_MAP_TYPE) != 0) {
-                       /* nothing to do here */
-               }
-               set_switch_time();
-       }
-
-       private void connectUsb(UsbDevice device) {
-               if (mService == null)
-                       pending_usb_device = device;
-               else {
-                       // Attempt to connect to the device
-                       try {
-                               mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device));
-                               AltosDebug.debug("Sent OPEN_USB message");
-                       } catch (RemoteException e) {
-                               AltosDebug.debug("connect device message failed");
-                       }
-               }
-       }
-
-       private void bluetoothEnabled(Intent data) {
-               try {
-                       mService.send(Message.obtain(null, TelemetryService.MSG_BLUETOOTH_ENABLED, null));
-               } catch (RemoteException e) {
-                       AltosDebug.debug("send BT enabled message failed");
-               }
-       }
-
-       private void connectDevice(Intent data) {
-               // Attempt to connect to the device
-               try {
-                       String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
-                       String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME);
-
-                       AltosDebug.debug("Connecting to " + address + " " + name);
-                       DeviceAddress   a = new DeviceAddress(address, name);
-                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a));
-                       AltosDebug.debug("Sent connecting message");
-               } catch (RemoteException e) {
-                       AltosDebug.debug("connect device message failed");
-               }
-       }
-
-       private void disconnectDevice(boolean remember) {
-               try {
-                       mService.send(Message.obtain(null, TelemetryService.MSG_DISCONNECT, (Boolean) remember));
-               } catch (RemoteException e) {
-               }
-       }
-
-       private void idle_mode(Intent data) {
-               int type = data.getIntExtra(IdleModeActivity.EXTRA_IDLE_RESULT, -1);
-               Message msg;
-
-               AltosDebug.debug("intent idle_mode %d", type);
-               switch (type) {
-               case IdleModeActivity.IDLE_MODE_CONNECT:
-                       msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_START);
-                       try {
-                               mService.send(msg);
-                       } catch (RemoteException re) {
-                       }
-                       break;
-               case IdleModeActivity.IDLE_MODE_DISCONNECT:
-                       msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_STOP);
-                       try {
-                               mService.send(msg);
-                       } catch (RemoteException re) {
-                       }
-                       break;
-               case IdleModeActivity.IDLE_MODE_REBOOT:
-                       msg = Message.obtain(null, TelemetryService.MSG_REBOOT);
-                       try {
-                               mService.send(msg);
-                       } catch (RemoteException re) {
-                       }
-                       break;
-               case IdleModeActivity.IDLE_MODE_IGNITERS:
-                       Intent serverIntent = new Intent(this, IgniterActivity.class);
-                       startActivityForResult(serverIntent, REQUEST_IGNITERS);
-                       break;
-               }
-       }
-
-       @Override
-       public boolean onCreateOptionsMenu(Menu menu) {
-               MenuInflater inflater = getMenuInflater();
-               inflater.inflate(R.menu.option_menu, menu);
-               return true;
-       }
-
-       void setFrequency(double freq) {
-               try {
-                       mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
-                       set_switch_time();
-               } catch (RemoteException e) {
-               }
-       }
-
-       void setFrequency(AltosFrequency frequency) {
-               setFrequency (frequency.frequency);
-       }
-
-       void setBaud(int baud) {
-               try {
-                       mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
-                       set_switch_time();
-               } catch (RemoteException e) {
-               }
-       }
-
-       void setBaud(String baud) {
-               try {
-                       int     value = Integer.parseInt(baud);
-                       int     rate = AltosLib.ao_telemetry_rate_38400;
-                       switch (value) {
-                       case 2400:
-                               rate = AltosLib.ao_telemetry_rate_2400;
-                               break;
-                       case 9600:
-                               rate = AltosLib.ao_telemetry_rate_9600;
-                               break;
-                       case 38400:
-                               rate = AltosLib.ao_telemetry_rate_38400;
-                               break;
-                       }
-                       setBaud(rate);
-               } catch (NumberFormatException e) {
-               }
-       }
-
-       void select_tracker(int serial) {
-               int i;
-
-               AltosDebug.debug("select tracker %d\n", serial);
-
-               if (serial == selected_serial) {
-                       AltosDebug.debug("%d already selected\n", serial);
-                       return;
-               }
-
-               if (serial != 0) {
-                       for (i = 0; i < trackers.length; i++)
-                               if (trackers[i].serial == serial)
-                                       break;
-
-                       if (i == trackers.length) {
-                               AltosDebug.debug("attempt to select unknown tracker %d\n", serial);
-                               return;
-                       }
-               }
-
-               current_serial = selected_serial = serial;
-               update_state(null);
-       }
-
-       void touch_trackers(Integer[] serials) {
-               AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this);
-               builder_tracker.setTitle("Select Tracker");
-
-               final Tracker[] my_trackers = new Tracker[serials.length + 1];
-
-               my_trackers[0] = new Tracker(null);
-
-               for (int i = 0; i < serials.length; i++) {
-                       AltosState      s = telemetry_state.states.get(serials[i]);
-                       my_trackers[i+1] = new Tracker(s);
-               }
-               builder_tracker.setItems(my_trackers,
-                                        new DialogInterface.OnClickListener() {
-                                                public void onClick(DialogInterface dialog, int item) {
-                                                        if (item == 0)
-                                                                select_tracker(0);
-                                                        else
-                                                                select_tracker(my_trackers[item].serial);
-                                                }
-                                        });
-               AlertDialog alert_tracker = builder_tracker.create();
-               alert_tracker.show();
-       }
-
-       void delete_track(int serial) {
-               try {
-                       mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
-               } catch (Exception ex) {
-               }
-       }
-
-       @Override
-       public boolean onOptionsItemSelected(MenuItem item) {
-               Intent serverIntent = null;
-               switch (item.getItemId()) {
-               case R.id.connect_scan:
-                       ensureBluetooth();
-                       // Launch the DeviceListActivity to see devices and do scan
-                       serverIntent = new Intent(this, DeviceListActivity.class);
-                       startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
-                       return true;
-               case R.id.disconnect:
-                       /* Disconnect the device
-                        */
-                       disconnectDevice(false);
-                       return true;
-               case R.id.quit:
-                       AltosDebug.debug("R.id.quit");
-                       disconnectDevice(true);
-                       finish();
-                       return true;
-               case R.id.setup:
-                       serverIntent = new Intent(this, SetupActivity.class);
-                       startActivityForResult(serverIntent, REQUEST_SETUP);
-                       return true;
-               case R.id.select_freq:
-                       // Set the TBT radio frequency
-
-                       final AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
-                       String[] frequency_strings = new String[frequencies.length];
-                       for (int i = 0; i < frequencies.length; i++)
-                               frequency_strings[i] = frequencies[i].toString();
-
-                       AlertDialog.Builder builder_freq = new AlertDialog.Builder(this);
-                       builder_freq.setTitle("Pick a frequency");
-                       builder_freq.setItems(frequency_strings,
-                                        new DialogInterface.OnClickListener() {
-                                                public void onClick(DialogInterface dialog, int item) {
-                                                        setFrequency(frequencies[item]);
-                                                }
-                                        });
-                       AlertDialog alert_freq = builder_freq.create();
-                       alert_freq.show();
-                       return true;
-               case R.id.select_tracker:
-                       if (trackers != null) {
-                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
-                               builder_serial.setTitle("Select a tracker");
-                               builder_serial.setItems(trackers,
-                                                       new DialogInterface.OnClickListener() {
-                                                               public void onClick(DialogInterface dialog, int item) {
-                                                                       System.out.printf("select item %d %s\n", item, trackers[item].display);
-                                                                       if (item == 0)
-                                                                               select_tracker(0);
-                                                                       else
-                                                                               select_tracker(trackers[item].serial);
-                                                               }
-                                                       });
-                               AlertDialog alert_serial = builder_serial.create();
-                               alert_serial.show();
-
-                       }
-                       return true;
-               case R.id.delete_track:
-                       if (trackers != null) {
-                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
-                               builder_serial.setTitle("Delete a track");
-                               final Tracker[] my_trackers = new Tracker[trackers.length - 1];
-                               for (int i = 0; i < trackers.length - 1; i++)
-                                       my_trackers[i] = trackers[i+1];
-                               builder_serial.setItems(my_trackers,
-                                                       new DialogInterface.OnClickListener() {
-                                                               public void onClick(DialogInterface dialog, int item) {
-                                                                       delete_track(my_trackers[item].serial);
-                                                               }
-                                                       });
-                               AlertDialog alert_serial = builder_serial.create();
-                               alert_serial.show();
-
-                       }
-                       return true;
-               case R.id.idle_mode:
-                       serverIntent = new Intent(this, IdleModeActivity.class);
-                       serverIntent.putExtra(EXTRA_IDLE_MODE, idle_mode);
-                       startActivityForResult(serverIntent, REQUEST_IDLE_MODE);
-                       return true;
-               }
-               return false;
-       }
-
-       static String direction(AltosGreatCircle from_receiver,
-                               Location receiver) {
-               if (from_receiver == null)
-                       return null;
-
-               if (receiver == null)
-                       return null;
-
-               if (!receiver.hasBearing())
-                       return null;
-
-               float   bearing = receiver.getBearing();
-               float   heading = (float) from_receiver.bearing - bearing;
-
-               while (heading <= -180.0f)
-                       heading += 360.0f;
-               while (heading > 180.0f)
-                       heading -= 360.0f;
-
-               int iheading = (int) (heading + 0.5f);
-
-               if (-1 < iheading && iheading < 1)
-                       return "ahead";
-               else if (iheading < -179 || 179 < iheading)
-                       return "backwards";
-               else if (iheading < 0)
-                       return String.format("left %d°", -iheading);
-               else
-                       return String.format("right %d°", iheading);
-       }
-
-       public void onLocationChanged(Location location) {
-               this.location = location;
-               AltosDebug.debug("Location changed to %f,%f",
-                                location.getLatitude(),
-                                location.getLongitude());
-               update_ui(telemetry_state, state, false);
-       }
-
-       public void onStatusChanged(String provider, int status, Bundle extras) {
-               AltosDebug.debug("Location status now %d\n", status);
-       }
-
-       public void onProviderEnabled(String provider) {
-               AltosDebug.debug("Location provider enabled %s\n", provider);
-       }
-
-       public void onProviderDisabled(String provider) {
-               AltosDebug.debug("Location provider disabled %s\n", provider);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java
deleted file mode 100644 (file)
index a443b6e..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.UUID;
-
-import android.os.Handler;
-
-import org.altusmetrum.altoslib_13.*;
-
-public abstract class AltosDroidLink extends AltosLink {
-
-       Handler         handler;
-
-       Thread          input_thread   = null;
-
-       public double frequency() {
-               return frequency;
-       }
-
-       public int telemetry_rate() {
-               return telemetry_rate;
-       }
-
-       public void save_frequency() {
-               AltosPreferences.set_frequency(0, frequency);
-       }
-
-       public void save_telemetry_rate() {
-               AltosPreferences.set_telemetry_rate(0, telemetry_rate);
-       }
-
-       Object closed_lock = new Object();
-       boolean closing = false;
-       boolean closed = false;
-
-       public boolean closed() {
-               synchronized(closed_lock) {
-                       return closing;
-               }
-       }
-
-       void connected() throws InterruptedException {
-               input_thread = new Thread(this);
-               input_thread.start();
-
-               // Configure the newly connected device for telemetry
-               print("~\nE 0\n");
-               set_monitor(false);
-               AltosDebug.debug("ConnectThread: connected");
-
-               /* Let TelemetryService know we're connected
-                */
-               handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
-
-               /* Notify other waiting threads that we're connected now
-                */
-               notifyAll();
-       }
-
-       public void closing() {
-               synchronized(closed_lock) {
-                       AltosDebug.debug("Marked closing true");
-                       closing = true;
-               }
-       }
-
-       private boolean actually_closed() {
-               synchronized(closed_lock) {
-                       return closed;
-               }
-       }
-
-       abstract void close_device();
-
-       public void close() {
-               AltosDebug.debug("close(): begin");
-
-               closing();
-
-               flush_output();
-
-               synchronized (closed_lock) {
-                       AltosDebug.debug("Marked closed true");
-                       closed = true;
-               }
-
-               close_device();
-
-               synchronized(this) {
-
-                       if (input_thread != null) {
-                               AltosDebug.debug("close(): stopping input_thread");
-                               try {
-                                       AltosDebug.debug("close(): input_thread.interrupt().....");
-                                       input_thread.interrupt();
-                                       AltosDebug.debug("close(): input_thread.join().....");
-                                       input_thread.join();
-                               } catch (Exception e) {}
-                               input_thread = null;
-                       }
-                       notifyAll();
-               }
-       }
-
-       abstract int write(byte[] buffer, int len);
-
-       abstract int read(byte[] buffer, int len);
-
-       private static final int buffer_size = 64;
-
-       private byte[] in_buffer = new byte[buffer_size];
-       private byte[] out_buffer = new byte[buffer_size];
-       private int buffer_len = 0;
-       private int buffer_off = 0;
-       private int out_buffer_off = 0;
-
-       private byte[] debug_chars = new byte[buffer_size];
-       private int debug_off;
-
-       private void debug_input(byte b) {
-               if (b == '\n') {
-                       AltosDebug.debug("            " + new String(debug_chars, 0, debug_off));
-                       debug_off = 0;
-               } else {
-                       if (debug_off < buffer_size)
-                               debug_chars[debug_off++] = b;
-               }
-       }
-
-       private void disconnected() {
-               if (closed()) {
-                       AltosDebug.debug("disconnected after closed");
-                       return;
-               }
-
-               AltosDebug.debug("Sending disconnected message");
-               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
-       }
-
-       public int getchar() {
-
-               if (actually_closed())
-                       return ERROR;
-
-               while (buffer_off == buffer_len) {
-                       buffer_len = read(in_buffer, buffer_size);
-                       if (buffer_len < 0) {
-                               AltosDebug.debug("ERROR returned from getchar()");
-                               disconnected();
-                               return ERROR;
-                       }
-                       buffer_off = 0;
-               }
-//             if (AltosDebug.D)
-//                     debug_input(in_buffer[buffer_off]);
-               return in_buffer[buffer_off++];
-       }
-
-       public void flush_output() {
-               super.flush_output();
-
-               if (actually_closed()) {
-                       out_buffer_off = 0;
-                       return;
-               }
-
-               while (out_buffer_off != 0) {
-                       int     sent = write(out_buffer, out_buffer_off);
-
-                       if (sent <= 0) {
-                               AltosDebug.debug("flush_output() failed");
-                               out_buffer_off = 0;
-                               break;
-                       }
-
-                       if (sent < out_buffer_off)
-                               System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
-
-                       out_buffer_off -= sent;
-               }
-       }
-
-       public void putchar(byte c) {
-               out_buffer[out_buffer_off++] = c;
-               if (out_buffer_off == buffer_size)
-                       flush_output();
-       }
-
-       public void print(String data) {
-               byte[] bytes = data.getBytes();
-//             AltosDebug.debug(data.replace('\n', '\\'));
-               for (byte b : bytes)
-                       putchar(b);
-       }
-
-       public AltosDroidLink(Handler handler) {
-               this.handler = handler;
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java
deleted file mode 100644 (file)
index 6da90a3..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import java.io.*;
-import android.location.Location;
-import org.altusmetrum.altoslib_13.*;
-
-public interface AltosDroidMapInterface {
-       public void onCreateView(AltosDroid altos_droid);
-
-       public void onDestroyView();
-
-       public void set_visible(boolean visible);
-
-       public void center(double lat, double lon, double accuracy);
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java
deleted file mode 100644 (file)
index 294094c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright © 2016 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-public interface AltosDroidMapSourceListener {
-       public void map_source_changed(int map_source);
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java
deleted file mode 100644 (file)
index 8bb78c0..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-package org.altusmetrum.AltosDroid;
-
-import java.io.*;
-import java.util.*;
-import java.text.*;
-
-import android.content.Context;
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosDroidPreferences extends AltosPreferences {
-
-       /* Active device preference name */
-       final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS";
-       final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME";
-
-       static DeviceAddress    active_device_address;
-
-       /* Map source preference name */
-       final static String mapSourcePreference = "MAP-SOURCE";
-
-       static final int        MAP_SOURCE_OFFLINE = 0;
-       static final int        MAP_SOURCE_ONLINE = 1;
-
-       static int      map_source;
-
-       public static void init(Context context) {
-               if (backend != null)
-                       return;
-
-               AltosPreferences.init(new AltosDroidPreferencesBackend(context));
-
-               String address = backend.getString(activeDeviceAddressPreference, null);
-               String name = backend.getString(activeDeviceNamePreference, null);
-
-               if (address != null && name != null)
-                       active_device_address = new DeviceAddress (address, name);
-
-               map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
-       }
-
-       public static void set_active_device(DeviceAddress address) {
-               synchronized(backend) {
-                       active_device_address = address;
-                       if (active_device_address != null) {
-                               backend.putString(activeDeviceAddressPreference, active_device_address.address);
-                               backend.putString(activeDeviceNamePreference, active_device_address.name);
-                       } else {
-                               backend.remove(activeDeviceAddressPreference);
-                               backend.remove(activeDeviceNamePreference);
-                       }
-                       flush_preferences();
-               }
-       }
-
-       public static DeviceAddress active_device() {
-               synchronized(backend) {
-                       return active_device_address;
-               }
-       }
-
-       static LinkedList<AltosDroidMapSourceListener> map_source_listeners;
-
-       public static void set_map_source(int map_source) {
-               synchronized(backend) {
-                       AltosDroidPreferences.map_source = map_source;
-                       backend.putInt(mapSourcePreference, map_source);
-                       flush_preferences();
-               }
-               if (map_source_listeners != null) {
-                       for (AltosDroidMapSourceListener l : map_source_listeners) {
-                               l.map_source_changed(map_source);
-                       }
-               }
-       }
-
-       public static int map_source() {
-               synchronized(backend) {
-                       return map_source;
-               }
-       }
-
-       public static void register_map_source_listener(AltosDroidMapSourceListener l) {
-               synchronized(backend) {
-                       if (map_source_listeners == null)
-                               map_source_listeners = new LinkedList<AltosDroidMapSourceListener>();
-                       map_source_listeners.add(l);
-               }
-       }
-
-       public static void unregister_map_source_listener(AltosDroidMapSourceListener l) {
-               synchronized(backend) {
-                       map_source_listeners.remove(l);
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
deleted file mode 100644 (file)
index 854fe86..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.io.File;
-import java.util.Map;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Environment;
-import android.util.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosDroidPreferencesBackend extends AltosPreferencesBackend {
-       public final static String        NAME    = "org.altusmetrum.AltosDroid";
-       private Context                   context = null;
-       private SharedPreferences         prefs   = null;
-       private SharedPreferences.Editor  editor  = null;
-
-       public AltosDroidPreferencesBackend(Context in_context) {
-               this(in_context, NAME);
-       }
-
-       public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
-               context = in_context;
-               prefs   = context.getSharedPreferences(in_prefs, 0);
-               editor  = prefs.edit();
-       }
-
-       public String[] keys() {
-               Map<String, ?> all = prefs.getAll();
-               Object[] ao = all.keySet().toArray();
-
-               String[] as = new String[ao.length];
-               for (int i = 0; i < ao.length; i++)
-                       as[i] = (String) ao[i];
-               return as;
-       }
-
-       public AltosPreferencesBackend node(String key) {
-               if (!nodeExists(key))
-                       putBoolean(key, true);
-               return new AltosDroidPreferencesBackend(context, key);
-       }
-
-       public boolean nodeExists(String key) {
-               return prefs.contains(key);
-       }
-
-       public boolean getBoolean(String key, boolean def) {
-               return prefs.getBoolean(key, def);
-       }
-
-       public double getDouble(String key, double def) {
-               Float f = Float.valueOf(prefs.getFloat(key, (float)def));
-               return f.doubleValue();
-       }
-
-       public int getInt(String key, int def) {
-               return prefs.getInt(key, def);
-       }
-
-       public String getString(String key, String def) {
-               String  ret;
-               ret = prefs.getString(key, def);
-//             AltosDebug.debug("AltosDroidPreferencesBackend get string %s:\n", key);
-//             if (ret == null)
-//                     AltosDebug.debug("      (null)\n");
-//             else {
-//                     String[] lines = ret.split("\n");
-//                     for (String l : lines)
-//                             AltosDebug.debug("        %s\n", l);
-//             }
-               return ret;
-       }
-
-       public byte[] getBytes(String key, byte[] def) {
-               String save = prefs.getString(key, null);
-
-               if (save == null)
-                       return def;
-
-               byte[] bytes = Base64.decode(save, Base64.DEFAULT);
-               return bytes;
-       }
-
-       public void putBoolean(String key, boolean value) {
-               editor.putBoolean(key, value);
-       }
-
-       public void putDouble(String key, double value) {
-               editor.putFloat(key, (float)value);
-       }
-
-       public void putInt(String key, int value) {
-               editor.putInt(key, value);
-       }
-
-       public void putString(String key, String value) {
-//             AltosDebug.debug("AltosDroidPreferencesBackend put string %s:\n", key);
-//             String[] lines = value.split("\n");
-//             for (String l : lines)
-//                     AltosDebug.debug("        %s\n", l);
-               editor.putString(key, value);
-       }
-
-       public void putBytes(String key, byte[] bytes) {
-               String save = Base64.encodeToString(bytes, Base64.DEFAULT);
-               editor.putString(key, save);
-       }
-
-       public void remove(String key) {
-               AltosDebug.debug("remove preference %s\n", key);
-               editor.remove(key);
-       }
-
-       public void flush() {
-               editor.apply();
-       }
-
-       public File homeDirectory() {
-               return Environment.getExternalStorageDirectory();
-       }
-
-       public void debug(String format, Object ... arguments) {
-               AltosDebug.debug(format, arguments);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
deleted file mode 100644 (file)
index 9594f85..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_13.*;
-import android.location.Location;
-import android.app.Activity;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.app.FragmentManager;
-import android.location.Location;
-import android.widget.TextView;
-
-public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
-       TelemetryState          last_telem_state;
-       AltosState              last_state;
-       AltosGreatCircle        last_from_receiver;
-       Location                last_receiver;
-       AltosDroid              altos_droid;
-
-       public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
-
-       public abstract String tab_name();
-
-       public void units_changed(boolean imperial_units) {
-               if (!isHidden())
-                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
-       }
-
-       public void set_value(TextView text_view,
-                             AltosUnits units,
-                             int width,
-                             double value) {
-               if (value == AltosLib.MISSING)
-                       text_view.setText("");
-               else
-                       text_view.setText(units.show(width, value));
-       }
-
-       public void set_visible(boolean visible) {
-               FragmentTransaction     ft = AltosDroid.fm.beginTransaction();
-               AltosDebug.debug("set visible %b %s\n", visible, tab_name());
-               if (visible) {
-                       ft.show(this);
-                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
-               } else
-                       ft.hide(this);
-               try {
-                       ft.commitAllowingStateLoss();
-               } catch (IllegalStateException ie) {
-               }
-       }
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               altos_droid = (AltosDroid) activity;
-               altos_droid.registerTab(this);
-       }
-
-       @Override
-       public void onDetach() {
-               super.onDetach();
-               altos_droid.unregisterTab(this);
-               altos_droid = null;
-       }
-
-       @Override
-       public void onResume() {
-               super.onResume();
-               AltosDebug.debug("onResume tab %s\n", tab_name());
-               set_visible(true);
-       }
-
-       public void update_ui(TelemetryState telem_state, AltosState state,
-                             AltosGreatCircle from_receiver, Location receiver, boolean is_current)
-       {
-               last_telem_state = telem_state;
-               last_state = state;
-               last_from_receiver = from_receiver;
-               last_receiver = receiver;
-               if (is_current)
-                       show(telem_state, state, from_receiver, receiver);
-               else
-                       return;
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java
deleted file mode 100644 (file)
index 1aebcd3..0000000
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import java.io.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.graphics.*;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.view.*;
-import android.widget.*;
-import android.location.Location;
-import android.content.*;
-import android.util.*;
-
-class Rocket implements Comparable {
-       AltosLatLon     position;
-       String          name;
-       int             serial;
-       long            last_packet;
-       boolean         active;
-       AltosMapOffline map_offline;
-
-       void paint() {
-               map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
-               map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
-       }
-
-       void set_position(AltosLatLon position, long last_packet) {
-               this.position = position;
-               this.last_packet = last_packet;
-       }
-
-       void set_active(boolean active) {
-               this.active = active;
-       }
-
-       public int compareTo(Object o) {
-               Rocket other = (Rocket) o;
-
-               if (active && !other.active)
-                       return 1;
-               if (other.active && !active)
-                       return -1;
-
-               long    diff = last_packet - other.last_packet;
-
-               if (diff > 0)
-                       return 1;
-               if (diff < 0)
-                       return -1;
-               return 0;
-       }
-
-       Rocket(int serial, AltosMapOffline map_offline) {
-               this.serial = serial;
-               this.name = String.format("%d", serial);
-               this.map_offline = map_offline;
-       }
-}
-
-public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface, AltosMapTypeListener {
-       ScaleGestureDetector    scale_detector;
-       boolean                 scaling;
-       AltosMap                map;
-       AltosDroid              altos_droid;
-
-       static int scale = 1;
-
-       AltosLatLon     here;
-       AltosLatLon     there;
-       AltosLatLon     pad;
-
-       Canvas  canvas;
-       Paint   paint;
-
-       Bitmap  pad_bitmap;
-       int     pad_off_x, pad_off_y;
-       Bitmap  rocket_bitmap;
-       int     rocket_off_x, rocket_off_y;
-       Bitmap  here_bitmap;
-       int     here_off_x, here_off_y;
-
-       static  final int       WHITE = 0xffffffff;
-       static  final int       RED   = 0xffff0000;
-       static  final int       PINK  = 0xffff8080;
-       static  final int       YELLOW= 0xffffff00;
-       static  final int       CYAN  = 0xff00ffff;
-       static  final int       BLUE  = 0xff0000ff;
-       static  final int       BLACK = 0xff000000;
-
-       public static final int stateColors[] = {
-               WHITE,  // startup
-               WHITE,  // idle
-               WHITE,  // pad
-               RED,    // boost
-               PINK,   // fast
-               YELLOW, // coast
-               CYAN,   // drogue
-               BLUE,   // main
-               BLACK,  // landed
-               BLACK,  // invalid
-               CYAN,   // stateless
-       };
-
-       /* AltosMapInterface */
-       public void debug(String format, Object ... arguments) {
-               AltosDebug.debug(format, arguments);
-       }
-
-       class MapTile extends AltosMapTile {
-               public void paint(AltosMapTransform t) {
-                       AltosPointInt           pt = new AltosPointInt(t.screen(upper_left));
-
-                       if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
-                               return;
-
-                       AltosImage              altos_image = this.get_image();
-
-                       MapImage                map_image = (MapImage) altos_image;
-
-                       Bitmap                  bitmap = null;
-
-                       if (map_image != null)
-                               bitmap = map_image.bitmap;
-
-                       if (bitmap != null) {
-                               canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
-                       } else {
-                               paint.setColor(0xff808080);
-                               canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
-                               if (t.has_location()) {
-                                       String  message = null;
-                                       switch (status) {
-                                       case AltosMapTile.fetching:
-                                               message = "Fetching...";
-                                               break;
-                                       case AltosMapTile.bad_request:
-                                               message = "Internal error";
-                                               break;
-                                       case AltosMapTile.failed:
-                                               message = "Network error";
-                                               break;
-                                       case AltosMapTile.forbidden:
-                                               message = "Outside of known launch areas";
-                                               break;
-                                       }
-                                       if (message != null) {
-                                               Rect    bounds = new Rect();
-                                               paint.getTextBounds(message, 0, message.length(), bounds);
-
-                                               int     width = bounds.right - bounds.left;
-                                               int     height = bounds.bottom - bounds.top;
-
-                                               float x = pt.x + px_size / 2.0f;
-                                               float y = pt.y + px_size / 2.0f;
-                                               x = x - width / 2.0f;
-                                               y = y + height / 2.0f;
-                                               paint.setColor(0xff000000);
-                                               canvas.drawText(message, 0, message.length(), x, y, paint);
-                                       }
-                               }
-                       }
-               }
-
-               public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
-                       super(cache, upper_left, center, zoom, maptype, px_size, scale);
-               }
-
-       }
-
-       public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
-               return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
-       }
-
-       public AltosMapPath new_path() {
-               return null;
-       }
-
-       public AltosMapLine new_line() {
-               return null;
-       }
-
-       class MapImage implements AltosImage {
-               public Bitmap   bitmap;
-
-               public void flush() {
-                       if (bitmap != null) {
-                               bitmap.recycle();
-                               bitmap = null;
-                       }
-               }
-
-               public MapImage(File file) {
-                       bitmap = BitmapFactory.decodeFile(file.getPath());
-               }
-       }
-
-       public AltosImage load_image(File file) throws Exception {
-               return new MapImage(file);
-       }
-
-       class MapMark extends AltosMapMark {
-               public void paint(AltosMapTransform t) {
-               }
-
-               MapMark(double lat, double lon, int state) {
-                       super(lat, lon, state);
-               }
-       }
-
-       public AltosMapMark new_mark(double lat, double lon, int state) {
-               return new MapMark(lat, lon, state);
-       }
-
-       public int width() {
-               return getWidth();
-       }
-
-       public int height() {
-               return getHeight();
-       }
-
-       public void repaint() {
-               postInvalidate();
-       }
-
-       public void repaint(AltosRectangle damage) {
-               postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
-       }
-
-       public void set_zoom_label(String label) {
-       }
-
-       public void select_object(AltosLatLon latlon) {
-               if (map.transform == null)
-                       return;
-               ArrayList<Integer>      near = new ArrayList<Integer>();
-
-               for (Rocket rocket : sorted_rockets()) {
-                       if (rocket.position == null) {
-                               debug("rocket %d has no position\n", rocket.serial);
-                               continue;
-                       }
-                       double distance = map.transform.hypot(latlon, rocket.position);
-                       debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
-                       if (distance < rocket_bitmap.getWidth() * 2.0) {
-                               debug("selecting %d\n", rocket.serial);
-                               near.add(rocket.serial);
-                       }
-               }
-               if (near.size() != 0)
-                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
-       }
-
-       class Line {
-               AltosLatLon     a, b;
-
-               void paint() {
-                       if (a != null && b != null) {
-                               AltosPointDouble        a_screen = map.transform.screen(a);
-                               AltosPointDouble        b_screen = map.transform.screen(b);
-                               paint.setColor(0xff8080ff);
-                               canvas.drawLine((float) a_screen.x, (float) a_screen.y,
-                                                   (float) b_screen.x, (float) b_screen.y,
-                                                   paint);
-                       }
-               }
-
-               void set_a(AltosLatLon a) {
-                       this.a = a;
-               }
-
-               void set_b(AltosLatLon b) {
-                       this.b = b;
-               }
-
-               Line() {
-               }
-       }
-
-       Line line = new Line();
-
-       int     stroke_width = 20;
-
-       void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
-               if (lat_lon != null && map != null && map.transform != null) {
-                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
-
-                       Rect    bounds = new Rect();
-                       paint.getTextBounds(text, 0, text.length(), bounds);
-
-                       int     width = bounds.right - bounds.left;
-                       int     height = bounds.bottom - bounds.top;
-
-                       float x = pt.x;
-                       float y = pt.y;
-                       x = x - width / 2.0f - off_x;
-                       y = y + height / 2.0f - off_y;
-                       paint.setColor(0xff000000);
-                       canvas.drawText(text, 0, text.length(), x, y, paint);
-               }
-       }
-
-       HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
-
-       void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
-               if (lat_lon != null && map != null && map.transform != null) {
-                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
-
-                       canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
-               }
-       }
-
-       private Rocket[] sorted_rockets() {
-               Rocket[]        rocket_array = rockets.values().toArray(new Rocket[0]);
-
-               Arrays.sort(rocket_array);
-               return rocket_array;
-       }
-
-       private void draw_positions() {
-               line.set_a(there);
-               line.set_b(here);
-               line.paint();
-               draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
-
-               for (Rocket rocket : sorted_rockets())
-                       rocket.paint();
-               draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
-       }
-
-       @Override public void invalidate() {
-               Rect r = new Rect();
-               getDrawingRect(r);
-               super.invalidate();
-       }
-
-       @Override public void invalidate(int l, int t, int r, int b) {
-               Rect rect = new Rect();
-               getDrawingRect(rect);
-               super.invalidate();
-       }
-
-       @Override
-       protected void onDraw(Canvas view_canvas) {
-               if (map == null) {
-                       debug("MapView draw without map\n");
-                       return;
-               }
-               canvas = view_canvas;
-               paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-               paint.setStrokeWidth(stroke_width);
-               paint.setStrokeCap(Paint.Cap.ROUND);
-               paint.setStrokeJoin(Paint.Join.ROUND);
-               paint.setTextSize(40);
-               map.paint();
-               draw_positions();
-               canvas = null;
-       }
-
-       public boolean onScale(ScaleGestureDetector detector) {
-               float   f = detector.getScaleFactor();
-
-               if (f <= 0.8) {
-                       map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
-                       return true;
-               }
-               if (f >= 1.2) {
-                       map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
-                       return true;
-               }
-               return false;
-       }
-
-       public boolean onScaleBegin(ScaleGestureDetector detector) {
-               return true;
-       }
-
-       public void onScaleEnd(ScaleGestureDetector detector) {
-       }
-
-       @Override
-       public boolean dispatchTouchEvent(MotionEvent event) {
-               scale_detector.onTouchEvent(event);
-
-               if (scale_detector.isInProgress()) {
-                       scaling = true;
-               }
-
-               if (scaling) {
-                       if (event.getAction() == MotionEvent.ACTION_UP) {
-                               scaling = false;
-                       }
-                       return true;
-               }
-
-               if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                       map.touch_start((int) event.getX(), (int) event.getY(), true);
-               } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                       map.touch_continue((int) event.getX(), (int) event.getY(), true);
-               } else if (event.getAction() == MotionEvent.ACTION_UP) {
-                       map.touch_stop((int) event.getX(), (int) event.getY(), true);
-               }
-               return true;
-       }
-
-       double  mapAccuracy;
-
-       public void center(double lat, double lon, double accuracy) {
-               if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
-                       if (map != null)
-                               map.maybe_centre(lat, lon);
-                       mapAccuracy = accuracy;
-               }
-       }
-
-       public void set_visible(boolean visible) {
-               if (visible)
-                       setVisibility(VISIBLE);
-               else
-                       setVisibility(GONE);
-       }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               boolean changed = false;
-
-               if (state != null) {
-                       map.show(state, null);
-                       if (state.pad_lat != AltosLib.MISSING && pad == null)
-                               pad = new AltosLatLon(state.pad_lat, state.pad_lon);
-               }
-
-               if (telem_state != null) {
-                       Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
-                       Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
-
-                       /* remove deleted keys */
-                       for (int serial : old_serial) {
-                               if (!telem_state.states.containsKey(serial))
-                                       rockets.remove(serial);
-                       }
-
-                       /* set remaining keys */
-
-                       for (int serial : new_serial) {
-                               Rocket          rocket;
-                               AltosState      t_state = telem_state.states.get(serial);
-                               if (rockets.containsKey(serial))
-                                       rocket = rockets.get(serial);
-                               else {
-                                       rocket = new Rocket(serial, this);
-                                       rockets.put(serial, rocket);
-                               }
-                               if (t_state.gps != null) {
-                                       AltosLatLon     latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
-                                       rocket.set_position(latlon, t_state.received_time);
-                                       if (state.cal_data().serial == serial)
-                                               there = latlon;
-                               }
-                               if (state != null)
-                                       rocket.set_active(state.cal_data().serial == serial);
-                       }
-               }
-               if (receiver != null) {
-                       AltosLatLon new_here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
-                       if (!new_here.equals(here)) {
-                               here = new_here;
-                               AltosDebug.debug("Location changed, redraw");
-                               repaint();
-                       }
-               }
-       }
-
-       public void onCreateView(AltosDroid altos_droid) {
-               this.altos_droid = altos_droid;
-               map = new AltosMap(this, scale);
-               AltosPreferences.register_map_type_listener(this);
-               map.set_maptype(AltosPreferences.map_type());
-
-               pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
-               /* arrow at the bottom of the launchpad image */
-               pad_off_x = pad_bitmap.getWidth() / 2;
-               pad_off_y = pad_bitmap.getHeight();
-
-               rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
-               /* arrow at the bottom of the rocket image */
-               rocket_off_x = rocket_bitmap.getWidth() / 2;
-               rocket_off_y = rocket_bitmap.getHeight();
-
-               here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
-               /* Center of the dot */
-               here_off_x = here_bitmap.getWidth() / 2;
-               here_off_y = here_bitmap.getHeight() / 2;
-       }
-
-       public void onDestroyView() {
-               AltosPreferences.unregister_map_type_listener(this);
-       }
-
-       public void map_type_changed(int map_type) {
-               if (map != null)
-                       map.set_maptype(map_type);
-       }
-
-       public AltosMapOffline(Context context, AttributeSet attrs) {
-               super(context, attrs);
-               this.altos_droid = altos_droid;
-               scale_detector = new ScaleGestureDetector(context, this);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java
deleted file mode 100644 (file)
index 37e4435..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-import com.google.android.gms.maps.*;
-import com.google.android.gms.maps.model.*;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.graphics.*;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-//import android.support.v4.app.FragmentTransaction;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.location.Location;
-import android.content.*;
-
-class RocketOnline implements Comparable {
-       Marker          marker;
-       int             serial;
-       long            last_packet;
-       int             size;
-
-       void set_position(AltosLatLon position, long last_packet) {
-               marker.setPosition(new LatLng(position.lat, position.lon));
-               this.last_packet = last_packet;
-       }
-
-       private Bitmap rocket_bitmap(Context context, String text) {
-
-               /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
-                */
-               Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
-               Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
-
-               Canvas canvas = new Canvas(bitmap);
-               Paint paint = new Paint();
-               paint.setTextSize(40);
-               paint.setColor(0xff000000);
-
-               Rect    bounds = new Rect();
-               paint.getTextBounds(text, 0, text.length(), bounds);
-
-               int     width = bounds.right - bounds.left;
-               int     height = bounds.bottom - bounds.top;
-
-               float x = bitmap.getWidth() / 2.0f - width / 2.0f;
-               float y = bitmap.getHeight() / 2.0f - height / 2.0f;
-
-               size = bitmap.getWidth();
-
-               canvas.drawText(text, 0, text.length(), x, y, paint);
-               return bitmap;
-       }
-
-       public void remove() {
-               marker.remove();
-       }
-
-       public int compareTo(Object o) {
-               RocketOnline other = (RocketOnline) o;
-
-               long    diff = last_packet - other.last_packet;
-
-               if (diff > 0)
-                       return 1;
-               if (diff < 0)
-                       return -1;
-               return 0;
-       }
-
-       RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
-               this.serial = serial;
-               String name = String.format("%d", serial);
-               this.marker = map.addMarker(new MarkerOptions()
-                                           .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
-                                           .position(new LatLng(lat, lon))
-                                           .visible(true));
-               this.last_packet = last_packet;
-       }
-}
-
-public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, AltosMapTypeListener {
-       public SupportMapFragment mMapFragment;
-       private GoogleMap mMap;
-       private boolean mapLoaded = false;
-       Context context;
-
-       private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
-       private Marker mPadMarker;
-       private boolean pad_set;
-       private Polyline mPolyline;
-
-       private View map_view;
-
-       private double mapAccuracy = -1;
-
-       private AltosLatLon my_position = null;
-       private AltosLatLon target_position = null;
-
-       private AltosDroid altos_droid;
-
-       public void onCreateView(AltosDroid altos_droid) {
-               this.altos_droid = altos_droid;
-               final int map_type = AltosPreferences.map_type();
-               AltosPreferences.register_map_type_listener(this);
-               mMapFragment = new SupportMapFragment() {
-                       @Override
-                       public void onActivityCreated(Bundle savedInstanceState) {
-                               super.onActivityCreated(savedInstanceState);
-                               setupMap(map_type);
-                       }
-                       @Override
-                       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-                               map_view = super.onCreateView(inflater, container, savedInstanceState);
-                               return map_view;
-                       }
-                       @Override
-                       public void onDestroyView() {
-                               super.onDestroyView();
-                               map_view = null;
-                       }
-               };
-       }
-
-       public void onDestroyView() {
-               AltosPreferences.unregister_map_type_listener(this);
-       }
-
-       private double pixel_distance(LatLng a, LatLng b) {
-               Projection projection = mMap.getProjection();
-
-               Point   a_pt = projection.toScreenLocation(a);
-               Point   b_pt = projection.toScreenLocation(b);
-
-               return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
-       }
-
-       private RocketOnline[] sorted_rockets() {
-               RocketOnline[]  rocket_array = rockets.values().toArray(new RocketOnline[0]);
-
-               Arrays.sort(rocket_array);
-               return rocket_array;
-       }
-
-       public void onMapClick(LatLng lat_lng) {
-               ArrayList<Integer>      near = new ArrayList<Integer>();
-
-               for (RocketOnline rocket : sorted_rockets()) {
-                       LatLng  pos = rocket.marker.getPosition();
-
-                       if (pos == null)
-                               continue;
-
-                       double distance = pixel_distance(lat_lng, pos);
-                       if (distance < rocket.size * 2)
-                               near.add(rocket.serial);
-               }
-
-               if (near.size() != 0)
-                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
-       }
-
-       public boolean onMarkerClick(Marker marker) {
-               onMapClick(marker.getPosition());
-               return true;
-       }
-
-       public void setupMap(int map_type) {
-               mMap = mMapFragment.getMap();
-               if (mMap != null) {
-                       map_type_changed(map_type);
-                       mMap.setMyLocationEnabled(true);
-                       mMap.getUiSettings().setTiltGesturesEnabled(false);
-                       mMap.getUiSettings().setZoomControlsEnabled(false);
-                       mMap.setOnMarkerClickListener(this);
-                       mMap.setOnMapClickListener(this);
-
-                       mPadMarker = mMap.addMarker(
-                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
-                                                          .position(new LatLng(0,0))
-                                                          .visible(false)
-                                       );
-
-                       mPolyline = mMap.addPolyline(
-                                       new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
-                                                            .width(20)
-                                                            .color(Color.BLUE)
-                                                            .visible(false)
-                                       );
-
-                       mapLoaded = true;
-               }
-       }
-
-       public void center(double lat, double lon, double accuracy) {
-               if (mMap == null)
-                       return;
-
-               if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
-                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
-                       mapAccuracy = accuracy;
-               }
-       }
-
-       private void set_rocket(int serial, AltosState state) {
-               RocketOnline    rocket;
-
-               if (state.gps == null || state.gps.lat == AltosLib.MISSING)
-                       return;
-
-               if (mMap == null)
-                       return;
-
-               if (rockets.containsKey(serial)) {
-                       rocket = rockets.get(serial);
-                       rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
-               } else {
-                       rocket = new RocketOnline(context,
-                                                 serial,
-                                                 mMap, state.gps.lat, state.gps.lon,
-                                                 state.received_time);
-                       rockets.put(serial, rocket);
-               }
-       }
-
-       private void remove_rocket(int serial) {
-               RocketOnline rocket = rockets.get(serial);
-               rocket.remove();
-               rockets.remove(serial);
-       }
-
-       public void set_visible(boolean visible) {
-               if (map_view == null)
-                       return;
-               if (visible)
-                       map_view.setVisibility(View.VISIBLE);
-               else
-                       map_view.setVisibility(View.GONE);
-       }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-
-               if (telem_state != null) {
-                       for (int serial : rockets.keySet()) {
-                               if (!telem_state.states.containsKey(serial))
-                                       remove_rocket(serial);
-                       }
-
-                       for (int serial : telem_state.states.keySet()) {
-                               set_rocket(serial, telem_state.states.get(serial));
-                       }
-               }
-
-               if (state != null) {
-                       if (mapLoaded) {
-                               if (!pad_set && state.pad_lat != AltosLib.MISSING) {
-                                       pad_set = true;
-                                       mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
-                                       mPadMarker.setVisible(true);
-                               }
-                       }
-                       if (state.gps != null && state.gps.lat != AltosLib.MISSING) {
-
-                               target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
-                               if (state.gps.locked && state.gps.nsat >= 4)
-                                       center (state.gps.lat, state.gps.lon, 10);
-                       }
-               }
-
-               if (receiver != null) {
-                       double accuracy;
-
-                       if (receiver.hasAccuracy())
-                               accuracy = receiver.getAccuracy();
-                       else
-                               accuracy = 1000;
-
-                       my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
-                       center (my_position.lat, my_position.lon, accuracy);
-               }
-
-               if (my_position != null && target_position != null && mPolyline != null) {
-                       mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
-                       mPolyline.setVisible(true);
-               }
-
-       }
-
-       public void map_type_changed(int map_type) {
-               if (mMap != null) {
-                       if (map_type == AltosMap.maptype_hybrid)
-                               mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
-                       else if (map_type == AltosMap.maptype_satellite)
-                               mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
-                       else if (map_type == AltosMap.maptype_terrain)
-                               mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
-                       else
-                               mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
-               }
-       }
-
-       public AltosMapOnline(Context context) {
-               this.context = context;
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java
deleted file mode 100644 (file)
index 0b235f2..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.UUID;
-import java.util.HashMap;
-
-import android.content.Context;
-import android.hardware.usb.*;
-import android.app.*;
-import android.os.Handler;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosUsb extends AltosDroidLink {
-
-       private Thread           input_thread   = null;
-
-       private Handler          handler;
-
-       private UsbManager              manager;
-       private UsbDevice               device;
-       private UsbDeviceConnection     connection;
-       private UsbInterface            iface;
-       private UsbEndpoint             in, out;
-
-       private InputStream      input;
-       private OutputStream     output;
-
-       // Constructor
-       public AltosUsb(Context context, UsbDevice device, Handler handler) {
-               super(handler);
-//             set_debug(D);
-               this.handler = handler;
-
-               iface = null;
-               in = null;
-               out = null;
-
-               int     niface = device.getInterfaceCount();
-
-               for (int i = 0; i < niface; i++) {
-
-                       iface = device.getInterface(i);
-
-                       in = null;
-                       out = null;
-
-                       int nendpoints = iface.getEndpointCount();
-
-                       for (int e = 0; e < nendpoints; e++) {
-                               UsbEndpoint     endpoint = iface.getEndpoint(e);
-
-                               if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
-                                       switch (endpoint.getDirection()) {
-                                       case UsbConstants.USB_DIR_OUT:
-                                               out = endpoint;
-                                               break;
-                                       case UsbConstants.USB_DIR_IN:
-                                               in = endpoint;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (in != null && out != null)
-                               break;
-               }
-
-               if (in != null && out != null) {
-                       AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
-
-                       manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
-
-                       if (manager == null) {
-                               AltosDebug.debug("USB_SERVICE failed");
-                               return;
-                       }
-
-                       connection = manager.openDevice(device);
-
-                       if (connection == null) {
-                               AltosDebug.debug("openDevice failed");
-                               return;
-                       }
-
-                       connection.claimInterface(iface, true);
-
-                       input_thread = new Thread(this);
-                       input_thread.start();
-
-                       // Configure the newly connected device for telemetry
-                       print("~\nE 0\n");
-                       set_monitor(false);
-               }
-       }
-
-       static private boolean isAltusMetrum(UsbDevice device) {
-               if (device.getVendorId() != AltosLib.vendor_altusmetrum)
-                       return false;
-               if (device.getProductId() < AltosLib.product_altusmetrum_min)
-                       return false;
-               if (device.getProductId() > AltosLib.product_altusmetrum_max)
-                       return false;
-               return true;
-       }
-
-       static boolean matchProduct(int want_product, UsbDevice device) {
-
-               if (!isAltusMetrum(device))
-                       return false;
-
-               if (want_product == AltosLib.product_any)
-                       return true;
-
-               int have_product = device.getProductId();
-
-               if (want_product == AltosLib.product_basestation)
-                       return have_product == AltosLib.product_teledongle ||
-                               have_product == AltosLib.product_teleterra ||
-                               have_product == AltosLib.product_telebt ||
-                               have_product == AltosLib.product_megadongle;
-
-               if (want_product == AltosLib.product_altimeter)
-                       return have_product == AltosLib.product_telemetrum ||
-                               have_product == AltosLib.product_telemega ||
-                               have_product == AltosLib.product_easymega ||
-                               have_product == AltosLib.product_telegps ||
-                               have_product == AltosLib.product_easymini ||
-                               have_product == AltosLib.product_telemini;
-
-               if (have_product == AltosLib.product_altusmetrum)       /* old devices match any request */
-                       return true;
-
-               if (want_product == have_product)
-                       return true;
-
-               return false;
-       }
-
-       static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
-               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
-
-//             if (manager.hasPermission(device))
-//                     return true;
-
-               AltosDebug.debug("request permission for USB device " + device.toString());
-
-               manager.requestPermission(device, pi);
-               return false;
-       }
-
-       static public UsbDevice find_device(Context context, int match_product) {
-               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
-
-               HashMap<String,UsbDevice>       devices = manager.getDeviceList();
-
-               for (UsbDevice  device : devices.values()) {
-                       int     vendor = device.getVendorId();
-                       int     product = device.getProductId();
-
-                       if (matchProduct(match_product, device)) {
-                               AltosDebug.debug("found USB device " + device.toString());
-                               return device;
-                       }
-               }
-
-               return null;
-       }
-
-       private void disconnected() {
-               if (closed()) {
-                       AltosDebug.debug("disconnected after closed");
-                       return;
-               }
-
-               AltosDebug.debug("Sending disconnected message");
-               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
-       }
-
-       void close_device() {
-               UsbDeviceConnection     tmp_connection;
-
-               synchronized(this) {
-                       tmp_connection = connection;
-                       connection = null;
-               }
-
-               if (tmp_connection != null) {
-                       AltosDebug.debug("Closing USB device");
-                       tmp_connection.close();
-               }
-       }
-
-       int read(byte[] buffer, int len) {
-               int ret = connection.bulkTransfer(in, buffer, len, -1);
-               AltosDebug.debug("read(%d) = %d\n", len, ret);
-               return ret;
-       }
-
-       int write(byte[] buffer, int len) {
-               int ret = connection.bulkTransfer(out, buffer, len, -1);
-               AltosDebug.debug("write(%d) = %d\n", len, ret);
-               return ret;
-       }
-
-       // Stubs of required methods when extending AltosLink
-       public boolean can_cancel_reply()   { return false; }
-       public boolean show_reply_timeout() { return true; }
-       public void hide_reply_timeout()    { }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java
deleted file mode 100644 (file)
index 039ba14..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import android.content.Context;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.View;
-
-public class AltosViewPager extends ViewPager {
-
-    public AltosViewPager(Context context) {
-        super(context);
-    }
-
-    public AltosViewPager(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
-
-           if (v.getClass() != null &&
-               v.getClass().getName() != null &&
-               v.getClass().getName().endsWith("MapOffline"))
-                   return true;
-
-           if(v.getClass() != null &&
-              v.getClass().getPackage() != null &&
-              v.getClass().getPackage().getName() != null &&
-              v.getClass().getPackage().getName().startsWith("maps."))
-                   return true;
-
-           return super.canScroll(v, checkV, dx, x, y);
-    }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java
deleted file mode 100644 (file)
index ae3299f..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import android.speech.tts.TextToSpeech;
-import android.speech.tts.TextToSpeech.OnInitListener;
-import android.location.Location;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class AltosVoice {
-
-       private TextToSpeech tts         = null;
-       private boolean      tts_enabled = false;
-
-       static final int TELL_MODE_NONE = 0;
-       static final int TELL_MODE_PAD = 1;
-       static final int TELL_MODE_FLIGHT = 2;
-       static final int TELL_MODE_RECOVER = 3;
-
-       static final int TELL_FLIGHT_NONE = 0;
-       static final int TELL_FLIGHT_STATE = 1;
-       static final int TELL_FLIGHT_SPEED = 2;
-       static final int TELL_FLIGHT_HEIGHT = 3;
-       static final int TELL_FLIGHT_TRACK = 4;
-
-       private int             last_tell_mode;
-       private int             last_tell_serial = AltosLib.MISSING;
-       private int             last_state;
-       private AltosGPS        last_gps;
-       private double          last_height = AltosLib.MISSING;
-       private Location        last_receiver;
-       private long            last_speak_time;
-       private int             last_flight_tell = TELL_FLIGHT_NONE;
-       private boolean         quiet = false;
-
-       private long now() {
-               return System.currentTimeMillis();
-       }
-
-       private void reset_last() {
-               last_tell_mode = TELL_MODE_NONE;
-               last_speak_time = now() - 100 * 1000;
-               last_gps = null;
-               last_height = AltosLib.MISSING;
-               last_receiver = null;
-               last_state = AltosLib.ao_flight_invalid;
-               last_flight_tell = TELL_FLIGHT_NONE;
-       }
-
-       public AltosVoice(AltosDroid a) {
-               tts = new TextToSpeech(a, new OnInitListener() {
-                       public void onInit(int status) {
-                               if (status == TextToSpeech.SUCCESS) tts_enabled = true;
-                       }
-               });
-               reset_last();
-       }
-
-       public synchronized void set_enable(boolean enable) {
-               tts_enabled = enable;
-       }
-
-       public synchronized void speak(String s) {
-               if (!tts_enabled) return;
-               last_speak_time = now();
-               if (!quiet)
-                       tts.speak(s, TextToSpeech.QUEUE_ADD, null);
-       }
-
-       public synchronized long time_since_speak() {
-               return now() - last_speak_time;
-       }
-
-       public synchronized void speak(String format, Object ... arguments) {
-               speak(String.format(format, arguments));
-       }
-
-       public synchronized boolean is_speaking() {
-               return tts.isSpeaking();
-       }
-
-       public void stop() {
-               if (tts != null) {
-                       tts.stop();
-                       tts.shutdown();
-               }
-       }
-
-       private boolean         last_apogee_good;
-       private boolean         last_main_good;
-       private boolean         last_gps_good;
-
-       private boolean tell_gonogo(String name,
-                                 boolean current,
-                                 boolean previous,
-                                 boolean new_mode) {
-               if (current != previous || new_mode)
-                       speak("%s %s.", name, current ? "ready" : "not ready");
-               return current;
-       }
-
-       private boolean tell_pad(TelemetryState telem_state, AltosState state,
-                             AltosGreatCircle from_receiver, Location receiver) {
-
-               if (state == null)
-                       return false;
-
-               AltosDebug.debug("tell_pad lag %b ltm %d\n", last_apogee_good, last_tell_mode);
-
-               if (state.apogee_voltage != AltosLib.MISSING)
-                       last_apogee_good = tell_gonogo("apogee",
-                                                      state.apogee_voltage >= AltosLib.ao_igniter_good,
-                                                      last_apogee_good,
-                                                      last_tell_mode != TELL_MODE_PAD);
-
-               if (state.main_voltage != AltosLib.MISSING)
-                       last_main_good = tell_gonogo("main",
-                                                    state.main_voltage >= AltosLib.ao_igniter_good,
-                                                    last_main_good,
-                                                    last_tell_mode != TELL_MODE_PAD);
-
-               if (state.gps != null)
-                       last_gps_good = tell_gonogo("G P S",
-                                                   state.gps_ready,
-                                                   last_gps_good,
-                                                   last_tell_mode != TELL_MODE_PAD);
-               return true;
-       }
-
-
-       private boolean descending(int state) {
-               return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
-       }
-
-       private boolean target_moved(AltosState state) {
-               if (last_gps != null && state != null && state.gps != null) {
-                       AltosGreatCircle        moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
-                                                                            state.gps.lat, state.gps.lon, state.gps.alt);
-                       double                  height_change = 0;
-                       double                  height = state.height();
-
-                       if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
-                               height_change = Math.abs(last_height - height);
-
-                       if (moved.range < 10 && height_change < 10)
-                               return false;
-               }
-               return true;
-       }
-
-       private boolean receiver_moved(Location receiver) {
-               if (last_receiver != null && receiver != null) {
-                       AltosGreatCircle        moved = new AltosGreatCircle(last_receiver.getLatitude(),
-                                                                            last_receiver.getLongitude(),
-                                                                            last_receiver.getAltitude(),
-                                                                            receiver.getLatitude(),
-                                                                            receiver.getLongitude(),
-                                                                            receiver.getAltitude());
-                       if (moved.range < 10)
-                               return false;
-               }
-               return true;
-       }
-
-       private boolean tell_flight(TelemetryState telem_state, AltosState state,
-                                   AltosGreatCircle from_receiver, Location receiver) {
-
-               boolean spoken = false;
-
-               if (state == null)
-                       return false;
-
-               if (last_tell_mode != TELL_MODE_FLIGHT)
-                       last_flight_tell = TELL_FLIGHT_NONE;
-
-               if (state.state() != last_state && AltosLib.ao_flight_boost <= state.state() && state.state() <= AltosLib.ao_flight_landed) {
-                       speak(state.state_name());
-                       if (descending(state.state()) && !descending(last_state)) {
-                               if (state.max_height() != AltosLib.MISSING) {
-                                       speak("max height: %s.",
-                                             AltosConvert.height.say_units(state.max_height()));
-                               }
-                       }
-                       last_flight_tell = TELL_FLIGHT_STATE;
-                       return true;
-               }
-
-               if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
-                       if (time_since_speak() < 10 * 1000)
-                               return false;
-                       if (!target_moved(state) && !receiver_moved(receiver))
-                               return false;
-               }
-
-               double  speed;
-               double  height;
-
-               if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
-                       last_flight_tell = TELL_FLIGHT_SPEED;
-
-                       if (state.state() <= AltosLib.ao_flight_coast) {
-                               speed = state.speed();
-                       } else {
-                               speed = state.gps_speed();
-                               if (speed == AltosLib.MISSING)
-                                       speed = state.speed();
-                       }
-
-                       if (speed != AltosLib.MISSING) {
-                               speak("speed: %s.", AltosConvert.speed.say_units(speed));
-                               return true;
-                       }
-               }
-
-               if (last_flight_tell == TELL_FLIGHT_SPEED) {
-                       last_flight_tell = TELL_FLIGHT_HEIGHT;
-                       height = state.height();
-
-                       if (height != AltosLib.MISSING) {
-                               speak("height: %s.", AltosConvert.height.say_units(height));
-                               return true;
-                       }
-               }
-
-               if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
-                       last_flight_tell = TELL_FLIGHT_TRACK;
-                       if (from_receiver != null) {
-                               speak("bearing %s %d, elevation %d, distance %s.",
-                                     from_receiver.bearing_words(
-                                             AltosGreatCircle.BEARING_VOICE),
-                                     (int) (from_receiver.bearing + 0.5),
-                                     (int) (from_receiver.elevation + 0.5),
-                                     AltosConvert.distance.say(from_receiver.distance));
-                               return true;
-                       }
-               }
-
-               return spoken;
-       }
-
-       private boolean tell_recover(TelemetryState telem_state, AltosState state,
-                                    AltosGreatCircle from_receiver, Location receiver) {
-
-               if (from_receiver == null)
-                       return false;
-
-               if (last_tell_mode == TELL_MODE_RECOVER) {
-                       if (!target_moved(state) && !receiver_moved(receiver))
-                               return false;
-                       if (time_since_speak() <= 10 * 1000)
-                               return false;
-               }
-
-               String direction = AltosDroid.direction(from_receiver, receiver);
-               if (direction == null)
-                       direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
-
-               speak("%s, distance %s.", direction,
-                     AltosConvert.distance.say_units(from_receiver.distance));
-
-               return true;
-       }
-
-       public void tell(TelemetryState telem_state, AltosState state,
-                        AltosGreatCircle from_receiver, Location receiver,
-                        AltosDroidTab tab, boolean quiet) {
-
-               this.quiet = quiet;
-
-               boolean spoken = false;
-
-               if (!tts_enabled) return;
-
-               if (is_speaking()) return;
-
-               int     tell_serial = last_tell_serial;
-
-               if (state != null)
-                       tell_serial = state.cal_data().serial;
-
-               if (tell_serial != last_tell_serial)
-                       reset_last();
-
-               int     tell_mode = TELL_MODE_NONE;
-
-               if (tab.tab_name().equals(AltosDroid.tab_pad_name))
-                       tell_mode = TELL_MODE_PAD;
-               else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
-                       tell_mode = TELL_MODE_FLIGHT;
-               else
-                       tell_mode = TELL_MODE_RECOVER;
-
-               if (tell_mode == TELL_MODE_PAD)
-                       spoken = tell_pad(telem_state, state, from_receiver, receiver);
-               else if (tell_mode == TELL_MODE_FLIGHT)
-                       spoken = tell_flight(telem_state, state, from_receiver, receiver);
-               else
-                       spoken = tell_recover(telem_state, state, from_receiver, receiver);
-
-               if (spoken) {
-                       last_tell_mode = tell_mode;
-                       last_tell_serial = tell_serial;
-                       if (state != null) {
-                               last_state = state.state();
-                               last_height = state.height();
-                               if (state.gps != null)
-                                       last_gps = state.gps;
-                       }
-                       if (receiver != null)
-                               last_receiver = receiver;
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/BuildInfo.java.in b/altosdroid/src/org/altusmetrum/AltosDroid/BuildInfo.java.in
deleted file mode 100644 (file)
index aa6c9a7..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-public class BuildInfo {
-       public static final String version      = "@VERSION@";
-       public static final String git_describe = "@DESCRIBE@";
-       public static final String branch       = "@BRANCH@";
-       public static final String commitnum    = "@COMMITNUM@";
-       public static final String commithash   = "@COMMITHASH@";
-       public static final String builddate    = "@BUILDDATE@";
-       public static final String buildtime    = "@BUILDTIME@";
-       public static final String buildtz      = "@BUILDTZ@";
-}
-
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java
deleted file mode 100644 (file)
index 6f84556..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-public class DeviceAddress {
-       public String   address;
-       public String   name;
-
-       public DeviceAddress(String address, String name) {
-               this.address = address;
-               this.name = name;
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java
deleted file mode 100644 (file)
index f36ef26..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.Set;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.AdapterView.OnItemClickListener;
-
-/**
- * This Activity appears as a dialog. It lists any paired devices and
- * devices detected in the area after discovery. When a device is chosen
- * by the user, the MAC address of the device is sent back to the parent
- * Activity in the result Intent.
- */
-public class DeviceListActivity extends Activity {
-
-       // Return Intent extra
-       public static final String EXTRA_DEVICE_ADDRESS = "device_address";
-       public static final String EXTRA_DEVICE_NAME = "device_name";
-
-       // Member fields
-       private BluetoothAdapter mBtAdapter;
-       private ArrayAdapter<String> mPairedDevicesArrayAdapter;
-       private ArrayAdapter<String> mNewDevicesArrayAdapter;
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.device_list);
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-
-               // Initialize the button to perform device discovery
-               Button scanButton = (Button) findViewById(R.id.button_scan);
-               scanButton.setOnClickListener(new OnClickListener() {
-                       public void onClick(View v) {
-                               doDiscovery();
-                               v.setVisibility(View.GONE);
-                       }
-               });
-
-               // Initialize array adapters. One for already paired devices and
-               // one for newly discovered devices
-               mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
-               mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
-
-               // Find and set up the ListView for paired devices
-               ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
-               pairedListView.setAdapter(mPairedDevicesArrayAdapter);
-               pairedListView.setOnItemClickListener(mDeviceClickListener);
-
-               // Find and set up the ListView for newly discovered devices
-               ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
-               newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
-               newDevicesListView.setOnItemClickListener(mDeviceClickListener);
-
-               // Register for broadcasts when a device is discovered
-               IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
-               this.registerReceiver(mReceiver, filter);
-
-               // Register for broadcasts when discovery has finished
-               filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
-               this.registerReceiver(mReceiver, filter);
-
-               // Get the local Bluetooth adapter
-               mBtAdapter = BluetoothAdapter.getDefaultAdapter();
-
-               // Get a set of currently paired devices
-               Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
-
-               // If there are paired devices, add each one to the ArrayAdapter
-               if (pairedDevices.size() > 0) {
-                       findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
-                       for (BluetoothDevice device : pairedDevices)
-                               if (device.getName().startsWith("TeleBT"))
-                                       mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
-
-               } else {
-                       String noDevices = getResources().getText(R.string.none_paired).toString();
-                       mPairedDevicesArrayAdapter.add(noDevices);
-               }
-       }
-
-       @Override
-       protected void onDestroy() {
-               super.onDestroy();
-
-               // Make sure we're not doing discovery anymore
-               if (mBtAdapter != null) {
-                       mBtAdapter.cancelDiscovery();
-               }
-
-               // Unregister broadcast listeners
-               this.unregisterReceiver(mReceiver);
-       }
-
-       /**
-       * Start device discover with the BluetoothAdapter
-       */
-       private void doDiscovery() {
-               AltosDebug.debug("doDiscovery()");
-
-               // Indicate scanning in the title
-               setProgressBarIndeterminateVisibility(true);
-               setTitle(R.string.scanning);
-
-               // Turn on sub-title for new devices
-               findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
-
-               // If we're already discovering, stop it
-               if (mBtAdapter.isDiscovering()) {
-                       mBtAdapter.cancelDiscovery();
-               }
-
-               // Request discover from BluetoothAdapter
-               mBtAdapter.startDiscovery();
-       }
-
-       // The on-click listener for all devices in the ListViews
-       private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
-               public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
-                       // Cancel discovery because it's costly and we're about to connect
-                       mBtAdapter.cancelDiscovery();
-
-                       // Get the device MAC address, which is the last 17 chars in the View
-                       String info = ((TextView) v).getText().toString();
-                       String address = info.substring(info.length() - 17);
-
-                       int newline = info.indexOf('\n');
-
-                       String name = null;
-                       if (newline > 0)
-                               name = info.substring(0, newline);
-                       else
-                               name = info;
-
-                       AltosDebug.debug("******* selected item '%s'", info);
-
-                       // Create the result Intent and include the MAC address
-                       Intent intent = new Intent();
-                       intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
-                       intent.putExtra(EXTRA_DEVICE_NAME, name);
-
-                       // Set result and finish this Activity
-                       setResult(Activity.RESULT_OK, intent);
-                       finish();
-               }
-       };
-
-       // The BroadcastReceiver that listens for discovered devices and
-       // changes the title when discovery is finished
-       private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-               @Override
-               public void onReceive(Context context, Intent intent) {
-                       String action = intent.getAction();
-
-                       // When discovery finds a device
-                       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
-
-                               /* Get the BluetoothDevice object from the Intent
-                                */
-                               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
-                               /* If it's already paired, skip it, because it's been listed already
-                                */
-                               if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
-                               {
-                                       String  name = device.getName();
-                                       if (name != null && name.startsWith("TeleBT"))
-                                               mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
-                               }
-
-                       /* When discovery is finished, change the Activity title
-                        */
-                       } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
-                               setProgressBarIndeterminateVisibility(false);
-                               setTitle(R.string.select_device);
-                               if (mNewDevicesArrayAdapter.getCount() == 0) {
-                                       String noDevices = getResources().getText(R.string.none_found).toString();
-                                       mNewDevicesArrayAdapter.add(noDevices);
-                               }
-                       }
-               }
-       };
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java b/altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java
deleted file mode 100644 (file)
index 2797fc5..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.altusmetrum.AltosDroid;
-
-       import java.lang.reflect.Array;
-       import java.lang.reflect.Field;
-       import java.util.HashMap;
-
-       public class Dumper {
-               private static Dumper instance = new Dumper();
-
-               protected static Dumper getInstance() {
-                       return instance;
-               }
-
-               class DumpContext {
-                       int maxDepth = 0;
-                       int maxArrayElements = 0;
-                       int callCount = 0;
-                       HashMap<String, String> ignoreList = new HashMap<String, String>();
-                       HashMap<Object, Integer> visited = new HashMap<Object, Integer>();
-               }
-
-               public static String dump(Object o) {
-                       return dump(o, 0, 0, null);
-               }
-
-               public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {
-                       DumpContext ctx = Dumper.getInstance().new DumpContext();
-                       ctx.maxDepth = maxDepth;
-                       ctx.maxArrayElements = maxArrayElements;
-
-                       if (ignoreList != null) {
-                               for (int i = 0; i < Array.getLength(ignoreList); i++) {
-                                       int colonIdx = ignoreList[i].indexOf(':');
-                                       if (colonIdx == -1)
-                                               ignoreList[i] = ignoreList[i] + ":";
-                                       ctx.ignoreList.put(ignoreList[i], ignoreList[i]);
-                               }
-                       }
-
-                       return dump(o, ctx);
-               }
-
-               protected static String dump(Object o, DumpContext ctx) {
-                       if (o == null) {
-                               return "<null>";
-                       }
-
-                       ctx.callCount++;
-                       StringBuffer tabs = new StringBuffer();
-                       for (int k = 0; k < ctx.callCount; k++) {
-                               tabs.append("\t");
-                       }
-                       StringBuffer buffer = new StringBuffer();
-                       @SuppressWarnings("rawtypes")
-                       Class oClass = o.getClass();
-
-                       String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);
-
-                       if (ctx.ignoreList.get(oSimpleName + ":") != null)
-                               return "<Ignored>";
-
-                       if (oClass.isArray()) {
-                               buffer.append("\n");
-                               buffer.append(tabs.toString().substring(1));
-                               buffer.append("[\n");
-                               int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));
-                               for (int i = 0; i < rowCount; i++) {
-                                       buffer.append(tabs.toString());
-                                       try {
-                                               Object value = Array.get(o, i);
-                                               buffer.append(dumpValue(value, ctx));
-                                       } catch (Exception e) {
-                                               buffer.append(e.getMessage());
-                                       }
-                                       if (i < Array.getLength(o) - 1)
-                                               buffer.append(",");
-                                       buffer.append("\n");
-                               }
-                               if (rowCount < Array.getLength(o)) {
-                                       buffer.append(tabs.toString());
-                                       buffer.append(Array.getLength(o) - rowCount + " more array elements...");
-                                       buffer.append("\n");
-                               }
-                               buffer.append(tabs.toString().substring(1));
-                               buffer.append("]");
-                       } else {
-                               buffer.append("\n");
-                               buffer.append(tabs.toString().substring(1));
-                               buffer.append("{\n");
-                               buffer.append(tabs.toString());
-                               buffer.append("hashCode: " + o.hashCode());
-                               buffer.append("\n");
-                               while (oClass != null && oClass != Object.class) {
-                                       Field[] fields = oClass.getDeclaredFields();
-
-                                       if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {
-                                               if (oClass != o.getClass()) {
-                                                       buffer.append(tabs.toString().substring(1));
-                                                       buffer.append("  Inherited from superclass " + oSimpleName + ":\n");
-                                               }
-
-                                               for (int i = 0; i < fields.length; i++) {
-
-                                                       String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());
-                                                       String fName = fields[i].getName();
-
-                                                       fields[i].setAccessible(true);
-                                                       buffer.append(tabs.toString());
-                                                       buffer.append(fName + "(" + fSimpleName + ")");
-                                                       buffer.append("=");
-
-                                                       if (ctx.ignoreList.get(":" + fName) == null &&
-                                                               ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&
-                                                               ctx.ignoreList.get(fSimpleName + ":") == null) {
-
-                                                               try {
-                                                                       Object value = fields[i].get(o);
-                                                                       buffer.append(dumpValue(value, ctx));
-                                                               } catch (Exception e) {
-                                                                       buffer.append(e.getMessage());
-                                                               }
-                                                               buffer.append("\n");
-                                                       } else {
-                                                               buffer.append("<Ignored>");
-                                                               buffer.append("\n");
-                                                       }
-                                               }
-                                               oClass = oClass.getSuperclass();
-                                               oSimpleName = oClass.getSimpleName();
-                                       } else {
-                                               oClass = null;
-                                               oSimpleName = "";
-                                       }
-                               }
-                               buffer.append(tabs.toString().substring(1));
-                               buffer.append("}");
-                       }
-                       ctx.callCount--;
-                       return buffer.toString();
-               }
-
-               protected static String dumpValue(Object value, DumpContext ctx) {
-                       if (value == null) {
-                               return "<null>";
-                       }
-                       if (value.getClass().isPrimitive() ||
-                               value.getClass() == java.lang.Short.class ||
-                               value.getClass() == java.lang.Long.class ||
-                               value.getClass() == java.lang.String.class ||
-                               value.getClass() == java.lang.Integer.class ||
-                               value.getClass() == java.lang.Float.class ||
-                               value.getClass() == java.lang.Byte.class ||
-                               value.getClass() == java.lang.Character.class ||
-                               value.getClass() == java.lang.Double.class ||
-                               value.getClass() == java.lang.Boolean.class) {
-
-                               return value.toString();
-
-                       } else {
-
-                               Integer visitedIndex = ctx.visited.get(value);
-                               if (visitedIndex == null) {
-                                       ctx.visited.put(value, ctx.callCount);
-                                       if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {
-                                               return dump(value, ctx);
-                                       } else {
-                                               return "<Reached max recursion depth>";
-                                       }
-                               } else {
-                                       return "<Previously visited - see hashCode " + value.hashCode() + ">";
-                               }
-                       }
-               }
-
-
-               private static String getSimpleNameWithoutArrayQualifier(@SuppressWarnings("rawtypes") Class clazz) {
-                       String simpleName = clazz.getSimpleName();
-                       int indexOfBracket = simpleName.indexOf('['); 
-                       if (indexOfBracket != -1)
-                               return simpleName.substring(0, indexOfBracket);
-                       return simpleName;
-               }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java
deleted file mode 100644 (file)
index c18d730..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
-import android.view.View;
-
-public class GoNoGoLights {
-       private Boolean state;
-       private Boolean missing;
-       private Boolean set;
-
-       private ImageView red;
-       private ImageView green;
-
-       private Drawable dRed;
-       private Drawable dGreen;
-       private Drawable dGray;
-
-       public GoNoGoLights(ImageView in_red, ImageView in_green, Resources r) {
-               red = in_red;
-               green = in_green;
-               state = false;
-               missing = true;
-               set = false;
-
-               dRed   = r.getDrawable(R.drawable.redled);
-               dGreen = r.getDrawable(R.drawable.greenled);
-               dGray  = r.getDrawable(R.drawable.grayled);
-       }
-
-       public void set(Boolean s, Boolean m) {
-               if (set && s == state && m == missing) return;
-               state = s;
-               missing = m;
-               set = true;
-               if (missing) {
-                       red.setImageDrawable(dGray);
-                       green.setImageDrawable(dGray);
-               } else if (state) {
-                       red.setImageDrawable(dGray);
-                       green.setImageDrawable(dGreen);
-               } else {
-                       red.setImageDrawable(dRed);
-                       green.setImageDrawable(dGray);
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java
deleted file mode 100644 (file)
index 3130d2a..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright © 2016 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.view.View.OnClickListener;
-import android.widget.*;
-import android.widget.AdapterView.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class IdleModeActivity extends Activity {
-       private EditText callsign;
-       private Button connect;
-       private Button disconnect;
-       private Button reboot;
-       private Button igniters;
-
-       public static final String EXTRA_IDLE_MODE = "idle_mode";
-       public static final String EXTRA_IDLE_RESULT = "idle_result";
-
-       public static final int IDLE_MODE_CONNECT = 1;
-       public static final int IDLE_MODE_REBOOT = 2;
-       public static final int IDLE_MODE_IGNITERS = 3;
-       public static final int IDLE_MODE_DISCONNECT = 4;
-
-       private void done(int type) {
-               AltosPreferences.set_callsign(callsign());
-               Intent intent = new Intent();
-               intent.putExtra(EXTRA_IDLE_RESULT, type);
-               setResult(Activity.RESULT_OK, intent);
-               finish();
-       }
-
-       private String callsign() {
-               return callsign.getEditableText().toString();
-       }
-
-       public void connect_idle() {
-               done(IDLE_MODE_CONNECT);
-       }
-
-       public void disconnect_idle() {
-               AltosDebug.debug("Disconnect idle button pressed");
-               done(IDLE_MODE_DISCONNECT);
-       }
-
-       public void reboot_idle() {
-               done(IDLE_MODE_REBOOT);
-       }
-
-       public void igniters_idle() {
-               done(IDLE_MODE_IGNITERS);
-       }
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.idle_mode);
-
-               callsign = (EditText) findViewById(R.id.set_callsign);
-               callsign.setText(new StringBuffer(AltosPreferences.callsign()));
-
-               connect = (Button) findViewById(R.id.connect_idle);
-               connect.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       connect_idle();
-                               }
-                       });
-               disconnect = (Button) findViewById(R.id.disconnect_idle);
-               disconnect.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       disconnect_idle();
-                               }
-                       });
-
-               boolean idle_mode = getIntent().getBooleanExtra(AltosDroid.EXTRA_IDLE_MODE, false);
-
-               if (idle_mode)
-                       connect.setVisibility(View.GONE);
-               else
-                       disconnect.setVisibility(View.GONE);
-
-               reboot = (Button) findViewById(R.id.reboot_idle);
-               reboot.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       reboot_idle();
-                               }
-                       });
-               igniters = (Button) findViewById(R.id.igniters_idle);
-               igniters.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       igniters_idle();
-                               }
-                       });
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java
deleted file mode 100644 (file)
index a172451..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright © 2016 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.lang.ref.WeakReference;
-import java.util.*;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.*;
-import android.graphics.*;
-import android.os.*;
-import android.view.*;
-import android.view.View.*;
-import android.widget.*;
-import android.widget.AdapterView.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-class IgniterItem {
-       public String name;
-       public String pretty;
-       public String status;
-       public LinearLayout igniter_view = null;
-       public TextView pretty_view = null;
-       public TextView status_view = null;
-
-       private void update() {
-               if (pretty_view != null)
-                       pretty_view.setText(pretty);
-               if (status_view != null)
-                       status_view.setText(status);
-       }
-
-       public void set(String name, String pretty, String status) {
-               if (!name.equals(this.name) ||
-                   !pretty.equals(this.pretty) ||
-                   !status.equals(this.status))
-               {
-                       this.name = name;
-                       this.pretty = pretty;
-                       this.status = status;
-                       update();
-               }
-       }
-
-       public void realize(LinearLayout igniter_view,
-                           TextView pretty_view,
-                           TextView status_view) {
-               if (igniter_view != this.igniter_view ||
-                   pretty_view != this.pretty_view ||
-                   status_view != this.status_view)
-               {
-                       this.igniter_view = igniter_view;
-                       this.pretty_view = pretty_view;
-                       this.status_view = status_view;
-                       update();
-               }
-       }
-
-       public IgniterItem() {
-       }
-}
-
-class IgniterAdapter extends ArrayAdapter<IgniterItem> {
-       int resource;
-       int selected_item = -1;
-
-       public IgniterAdapter(Context context, int in_resource) {
-               super(context, in_resource);
-               resource = in_resource;
-       }
-
-       @Override
-       public View getView(int position, View convertView, ViewGroup parent) {
-               IgniterItem item = getItem(position);
-               if (item.igniter_view == null) {
-                       LinearLayout igniter_view = new LinearLayout(getContext());
-                       String inflater = Context.LAYOUT_INFLATER_SERVICE;
-                       LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
-                       li.inflate(resource, igniter_view, true);
-
-                       item.realize(igniter_view,
-                                    (TextView) igniter_view.findViewById(R.id.igniter_name),
-                                    (TextView) igniter_view.findViewById(R.id.igniter_status));
-               }
-               if (position == selected_item)
-                       item.igniter_view.setBackgroundColor(Color.RED);
-               else
-                       item.igniter_view.setBackgroundColor(Color.BLACK);
-               return item.igniter_view;
-       }
-}
-
-public class IgniterActivity extends Activity {
-       private ListView igniters_view;
-       private ToggleButton arm;
-       private Button fire;
-
-       private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();;
-
-       private IgniterAdapter igniters_adapter;
-
-       private boolean is_bound;
-       private Messenger service = null;
-       private final Messenger messenger = new Messenger(new IncomingHandler(this));
-
-       private Timer query_timer;
-       private boolean query_timer_running;
-
-       private Timer arm_timer;
-       private int arm_remaining;
-
-       public static final int IGNITER_QUERY = 1;
-       public static final int IGNITER_FIRE = 2;
-
-       // The Handler that gets information back from the Telemetry Service
-       static class IncomingHandler extends Handler {
-               private final WeakReference<IgniterActivity> igniter_activity;
-               IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); }
-
-               @Override
-               public void handleMessage(Message msg) {
-                       IgniterActivity ia = igniter_activity.get();
-
-                       switch (msg.what) {
-                       case AltosDroid.MSG_IGNITER_STATUS:
-                               ia.igniter_status((HashMap <String,Integer>) msg.obj);
-                               break;
-                       }
-               }
-       };
-
-
-       private ServiceConnection connection = new ServiceConnection() {
-               public void onServiceConnected(ComponentName className, IBinder binder) {
-                       service = new Messenger(binder);
-                       query_timer_tick();
-               }
-
-               public void onServiceDisconnected(ComponentName className) {
-                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
-                       service = null;
-               }
-       };
-
-       void doBindService() {
-               bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
-               is_bound = true;
-       }
-
-       void doUnbindService() {
-               if (is_bound) {
-                       // If we have received the service, and hence registered with it, then now is the time to unregister.
-                       unbindService(connection);
-                       is_bound = false;
-               }
-       }
-
-       private void done() {
-               Intent intent = new Intent();
-               setResult(Activity.RESULT_OK, intent);
-               finish();
-       }
-
-       class FireThread extends Thread {
-               private final String igniter;
-
-               @Override
-               public void run() {
-                       Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter);
-                       try {
-                               service.send(msg);
-                       } catch (RemoteException re) {
-                       }
-               }
-
-               public FireThread(String igniter) {
-                       this.igniter = igniter;
-               }
-       }
-
-       private void fire_igniter() {
-               if (igniters_adapter.selected_item >= 0) {
-                       IgniterItem     item = igniters_adapter.getItem(igniters_adapter.selected_item);
-                       FireThread      ft = new FireThread(item.name);
-                       ft.run();
-                       arm.setChecked(false);
-               }
-       }
-
-       private void arm_igniter(boolean is_checked) {
-               if (is_checked) {
-                       arm_timer_stop();
-                       arm_timer = new Timer();
-                       arm_remaining = 10;
-                       arm_set_text();
-                       fire.setEnabled(true);
-                       arm_timer.scheduleAtFixedRate(new TimerTask() {
-                                       public void run() {
-                                               arm_timer_tick();
-                                       }},
-                               1000L, 1000L);
-               } else {
-                       arm_timer_stop();
-                       fire.setEnabled(false);
-               }
-       }
-
-       private synchronized void query_timer_tick() {
-               if (query_timer_running)
-                       return;
-               if (service == null)
-                       return;
-               query_timer_running = true;
-               Thread thread = new Thread(new Runnable() {
-                               public void run() {
-                                       try {
-                                               Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY);
-                                               msg.replyTo = messenger;
-                                               if (service == null) {
-                                                       synchronized(IgniterActivity.this) {
-                                                               query_timer_running = false;
-                                                       }
-                                               } else
-                                                       service.send(msg);
-                                       } catch (RemoteException re) {
-                                               AltosDebug.debug("igniter query thread failed");
-                                               synchronized(IgniterActivity.this) {
-                                                       query_timer_running = false;
-                                               }
-                                       }
-                               }
-                       });
-               thread.start();
-       }
-
-       private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) {
-               if (!status.containsKey(name))
-                       return false;
-
-               IgniterItem item;
-               if (!igniters.containsKey(name)) {
-                       item = new IgniterItem();
-                       igniters.put(name, item);
-                       igniters_adapter.add(item);
-               } else
-                       item = igniters.get(name);
-
-               item.set(name, pretty, AltosIgnite.status_string(status.get(name)));
-               return true;
-       }
-
-       private synchronized void igniter_status(HashMap <String,Integer> status) {
-               query_timer_running = false;
-               if (status == null) {
-                       AltosDebug.debug("no igniter status");
-                       return;
-               }
-               set_igniter(status, "drogue", "Apogee");
-               set_igniter(status, "main", "Main");
-               for (int extra = 0;; extra++) {
-                       String  name = String.format("%d", extra);
-                       String  pretty = String.format("%c", 'A' + extra);
-                       if (!set_igniter(status, name, pretty))
-                               break;
-               }
-       }
-
-       private synchronized void arm_timer_stop() {
-               if (arm_timer != null) {
-                       arm_timer.cancel();
-                       arm_timer = null;
-               }
-               arm_remaining = 0;
-       }
-
-       private void arm_set_text() {
-               String  text = String.format("Armed %d", arm_remaining);
-
-               if (arm.isChecked())
-                       arm.setText(text);
-               arm.setTextOn(text);
-       }
-
-       private void arm_timer_tick() {
-               --arm_remaining;
-               if (arm_remaining <= 0) {
-                       arm_timer_stop();
-                       runOnUiThread(new Runnable() {
-                                       public void run() {
-                                               arm.setChecked(false);
-                                               fire.setEnabled(false);
-                                       }
-                               });
-               } else {
-                       runOnUiThread(new Runnable() {
-                                       public void run() {
-                                               arm_set_text();
-                                       }
-                               });
-               }
-       }
-
-       private void select_item(int position) {
-               if (position != igniters_adapter.selected_item) {
-                       if (igniters_adapter.selected_item >= 0)
-                               igniters_view.setItemChecked(igniters_adapter.selected_item, false);
-                       if (position >= 0) {
-                               igniters_view.setItemChecked(position, true);
-                               arm.setEnabled(true);
-                       } else
-                               arm.setEnabled(false);
-                       igniters_adapter.selected_item = position;
-               }
-       }
-
-       private class IgniterItemClickListener implements ListView.OnItemClickListener {
-               @Override
-               public void onItemClick(AdapterView<?> av, View v, int position, long id) {
-                       AltosDebug.debug("select %d\n", position);
-                       select_item(position);
-               }
-       }
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.igniters);
-
-               igniters_view = (ListView) findViewById(R.id.igniters);
-               igniters_view.setClickable(true);
-
-               igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status);
-
-               igniters_view.setAdapter(igniters_adapter);
-               igniters_view.setOnItemClickListener(new IgniterItemClickListener());
-
-               fire = (Button) findViewById(R.id.igniter_fire);
-               fire.setEnabled(false);
-               fire.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       fire_igniter();
-                               }
-                       });
-
-               arm = (ToggleButton) findViewById(R.id.igniter_arm);
-               arm.setEnabled(false);
-               arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
-                               public void onCheckedChanged(CompoundButton v, boolean is_checked) {
-                                       arm_igniter(is_checked);
-                               }
-                       });
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-       }
-
-       @Override
-       protected void onStart() {
-               super.onStart();
-               doBindService();
-       }
-
-       @Override
-       protected void onResume() {
-               super.onResume();
-               query_timer = new Timer(true);
-               query_timer.scheduleAtFixedRate(new TimerTask() {
-                               public void run() {
-                                       query_timer_tick();
-                               }},
-                       0L, 5000L);
-       }
-
-       @Override
-       protected void onPause() {
-               super.onPause();
-               if (query_timer != null) {
-                       query_timer.cancel();
-                       query_timer = null;
-               }
-               arm_timer_stop();
-               arm.setChecked(false);
-               fire.setEnabled(false);
-       }
-
-       @Override
-       protected void onStop() {
-               super.onStop();
-               doUnbindService();
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java
deleted file mode 100644 (file)
index d510116..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright © 2016 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.lang.ref.WeakReference;
-import java.util.*;
-import java.text.*;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.content.*;
-import android.graphics.*;
-import android.os.*;
-import android.view.*;
-import android.view.View.*;
-import android.view.inputmethod.*;
-import android.widget.*;
-import android.widget.AdapterView.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-class FrequencyItem {
-       public AltosFrequency frequency;
-       public LinearLayout frequency_view = null;
-       public TextView pretty_view = null;
-
-       private void update() {
-               if (pretty_view != null && frequency != null)
-                       pretty_view.setText(frequency.toString());
-       }
-
-       public void realize(LinearLayout frequency_view,
-                           TextView pretty_view) {
-               if (frequency_view != this.frequency_view ||
-                   pretty_view != this.pretty_view)
-               {
-                       this.frequency_view = frequency_view;
-                       this.pretty_view = pretty_view;
-                       update();
-               }
-       }
-
-       public void set_frequency(AltosFrequency frequency) {
-               this.frequency = frequency;
-               update();
-       }
-
-       public FrequencyItem(AltosFrequency frequency) {
-               this.frequency = frequency;
-       }
-}
-
-class FrequencyAdapter extends ArrayAdapter<FrequencyItem> {
-       int resource;
-       int selected_item = -1;
-
-       public FrequencyAdapter(Context context, int in_resource) {
-               super(context, in_resource);
-               resource = in_resource;
-       }
-
-       public int count() {
-               int     count;
-
-               for (count = 0;; count++) {
-                       try {
-                               getItem(count);
-                       } catch (IndexOutOfBoundsException ie) {
-                               return count;
-                       }
-               }
-       }
-
-       @Override
-       public View getView(int position, View convertView, ViewGroup parent) {
-               FrequencyItem item = getItem(position);
-               if (item.frequency_view == null) {
-                       LinearLayout frequency_view = new LinearLayout(getContext());
-                       String inflater = Context.LAYOUT_INFLATER_SERVICE;
-                       LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
-                       li.inflate(resource, frequency_view, true);
-
-                       item.realize(frequency_view,
-                                    (TextView) frequency_view.findViewById(R.id.frequency));
-               }
-               if (position == selected_item)
-                       item.frequency_view.setBackgroundColor(Color.RED);
-               else
-                       item.frequency_view.setBackgroundColor(Color.BLACK);
-               return item.frequency_view;
-       }
-}
-
-public class ManageFrequenciesActivity extends Activity {
-       private ListView frequencies_view;
-
-       private Button set;
-       private Button remove;
-       private Button done;
-
-       private EditText set_frequency;
-       private EditText set_description;
-
-       private HashMap<String,FrequencyItem> frequencies = new HashMap<String,FrequencyItem>();;
-
-       private FrequencyAdapter frequencies_adapter;
-
-       private boolean is_bound;
-       private boolean changed = false;
-
-       private void done() {
-
-               set();
-
-               if (changed) {
-                       AltosFrequency[] frequencies = new AltosFrequency[frequencies_adapter.count()];
-                       for (int i = 0; i < frequencies.length; i++)
-                               frequencies[i] = frequencies_adapter.getItem(i).frequency;
-                       AltosPreferences.set_common_frequencies(frequencies);
-               }
-
-               Intent intent = new Intent();
-               setResult(Activity.RESULT_OK, intent);
-               finish();
-       }
-
-       private void load_item() {
-               if (frequencies_adapter.selected_item >= 0) {
-                       FrequencyItem item = frequencies_adapter.getItem(frequencies_adapter.selected_item);
-
-                       set_frequency.setText(item.frequency.frequency_string());
-                       set_description.setText(item.frequency.description);
-               } else {
-                       set_frequency.setText("");
-                       set_description.setText("");
-               }
-       }
-
-       private void select_item(int position) {
-               if (position != frequencies_adapter.selected_item) {
-                       if (frequencies_adapter.selected_item >= 0)
-                               frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
-                       if (position >= 0)
-                               frequencies_view.setItemChecked(position, true);
-                       frequencies_adapter.selected_item = position;
-               } else {
-                       if (frequencies_adapter.selected_item >= 0)
-                               frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
-                       frequencies_adapter.selected_item = -1;
-               }
-               load_item();
-       }
-
-       private int find(AltosFrequency frequency) {
-               for (int pos = 0; pos < frequencies_adapter.getCount(); pos++) {
-                       FrequencyItem   item = frequencies_adapter.getItem(pos);
-                       if (item.frequency.frequency == frequency.frequency &&
-                           item.frequency.description.equals(frequency.description))
-                               return pos;
-               }
-               return -1;
-       }
-
-       private int insert_item(AltosFrequency frequency) {
-               FrequencyItem new_item = new FrequencyItem(frequency);
-               int     pos;
-               for (pos = 0; pos < frequencies_adapter.getCount(); pos++) {
-                       FrequencyItem   item = frequencies_adapter.getItem(pos);
-                       if (item.frequency.frequency == new_item.frequency.frequency) {
-                               item.set_frequency(frequency);
-                               return pos;
-                       }
-                       if (item.frequency.frequency > new_item.frequency.frequency)
-                               break;
-               }
-               frequencies_adapter.insert(new_item, pos);
-               return pos;
-       }
-
-       private class FrequencyItemClickListener implements ListView.OnItemClickListener {
-               @Override
-               public void onItemClick(AdapterView<?> av, View v, int position, long id) {
-                       select_item(position);
-               }
-       }
-
-       private void hide_keyboard() {
-               InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
-               View view = getCurrentFocus();
-               if (view != null)
-                       imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
-       }
-
-       private void set() {
-               String  frequency_text = set_frequency.getEditableText().toString();
-               String  description_text = set_description.getEditableText().toString();
-
-               try {
-                       double  f = AltosParse.parse_double_locale(frequency_text);
-                       AltosFrequency frequency = new AltosFrequency(f, description_text);
-                       int pos;
-
-                       pos = find(frequency);
-                       if (pos < 0) {
-                               pos = insert_item(frequency);
-                               changed = true;
-                       }
-                       frequencies_adapter.selected_item = -1;
-                       select_item(pos);
-               } catch (ParseException pe) {
-               }
-               hide_keyboard();
-       }
-
-       private void remove() {
-               if (frequencies_adapter.selected_item >= 0) {
-                       frequencies_adapter.remove(frequencies_adapter.getItem(frequencies_adapter.selected_item));
-                       select_item(-1);
-                       frequencies_view.setAdapter(frequencies_adapter);
-                       changed = true;
-               }
-       }
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.manage_frequencies);
-
-               frequencies_view = (ListView) findViewById(R.id.frequencies);
-               frequencies_view.setClickable(true);
-
-               frequencies_adapter = new FrequencyAdapter(this, R.layout.frequency);
-
-               frequencies_view.setAdapter(frequencies_adapter);
-               frequencies_view.setOnItemClickListener(new FrequencyItemClickListener());
-
-               AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
-               for (AltosFrequency frequency : frequencies)
-                       insert_item(frequency);
-
-               set_frequency = (EditText) findViewById(R.id.set_frequency);
-               set_description = (EditText) findViewById(R.id.set_description);
-
-               set = (Button) findViewById(R.id.set);
-               set.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       set();
-                               }
-                       });
-
-               remove = (Button) findViewById(R.id.remove);
-               remove.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       remove();
-                               }
-                       });
-
-               done = (Button) findViewById(R.id.done);
-               done.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       done();
-                               }
-                       });
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-       }
-
-       @Override
-       protected void onStart() {
-               super.onStart();
-       }
-
-       @Override
-       protected void onResume() {
-               super.onResume();
-       }
-
-       @Override
-       protected void onPause() {
-               super.onPause();
-       }
-
-       @Override
-       protected void onStop() {
-               super.onStop();
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java
deleted file mode 100644 (file)
index ec88d2d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.view.View.OnClickListener;
-import android.widget.*;
-import android.widget.AdapterView.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class MapTypeActivity extends Activity {
-       private Button hybrid;
-       private Button satellite;
-       private Button roadmap;
-       private Button terrain;
-       private int selected_type;
-
-       public static final String EXTRA_MAP_TYPE = "map_type";
-
-       private void done(int type) {
-
-               Intent intent = new Intent();
-               intent.putExtra(EXTRA_MAP_TYPE, type);
-               setResult(Activity.RESULT_OK, intent);
-               finish();
-       }
-
-       public void selectType(View view) {
-               AltosDebug.debug("selectType %s", view.toString());
-               if (view == hybrid)
-                       done(AltosMap.maptype_hybrid);
-               if (view == satellite)
-                       done(AltosMap.maptype_satellite);
-               if (view == roadmap)
-                       done(AltosMap.maptype_roadmap);
-               if (view == terrain)
-                       done(AltosMap.maptype_terrain);
-       }
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.map_type);
-
-               hybrid = (Button) findViewById(R.id.map_type_hybrid);
-               satellite = (Button) findViewById(R.id.map_type_satellite);
-               roadmap = (Button) findViewById(R.id.map_type_roadmap);
-               terrain = (Button) findViewById(R.id.map_type_terrain);
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java
deleted file mode 100644 (file)
index e393b56..0000000
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import java.io.*;
-import java.text.*;
-
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.view.View.OnClickListener;
-import android.widget.*;
-import android.widget.AdapterView.*;
-import android.location.Location;
-import android.location.LocationManager;
-import android.location.LocationListener;
-import android.location.Criteria;
-
-import org.altusmetrum.altoslib_13.*;
-
-/**
- * This Activity appears as a dialog. It lists any paired devices and
- * devices detected in the area after discovery. When a device is chosen
- * by the user, the MAC address of the device is sent back to the parent
- * Activity in the result Intent.
- */
-public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapLoaderListener, LocationListener {
-
-       private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
-
-/*
-       private CheckBox        hybrid;
-       private CheckBox        satellite;
-       private CheckBox        roadmap;
-       private CheckBox        terrain;
-*/
-
-       private Spinner         known_sites_spinner;
-       private Spinner         min_zoom;
-       private Spinner         max_zoom;
-       private TextView        radius_label;
-       private Spinner         radius;
-
-       private EditText        latitude;
-       private EditText        longitude;
-
-       private ProgressBar     progress;
-
-       private AltosMapLoader  loader;
-
-       long    loader_notify_time;
-
-       /* AltosMapLoaderListener interfaces */
-       public void loader_start(final int max) {
-               loader_notify_time = System.currentTimeMillis();
-
-               this.runOnUiThread(new Runnable() {
-                               public void run() {
-                                       progress.setMax(max);
-                                       progress.setProgress(0);
-                               }
-                       });
-       }
-
-       public void loader_notify(final int cur, final int max, final String name) {
-               long    now = System.currentTimeMillis();
-
-               if (now - loader_notify_time < 100)
-                       return;
-
-               loader_notify_time = now;
-
-               this.runOnUiThread(new Runnable() {
-                               public void run() {
-                                       progress.setProgress(cur);
-                               }
-                       });
-       }
-
-       public void loader_done(int max) {
-               loader = null;
-               this.runOnUiThread(new Runnable() {
-                               public void run() {
-                                       progress.setProgress(0);
-                                       finish();
-                               }
-                       });
-       }
-
-       public void debug(String format, Object ... arguments) {
-               AltosDebug.debug(format, arguments);
-       }
-
-       /* AltosLaunchSiteListener interface */
-
-       public void notify_launch_sites(final List<AltosLaunchSite> sites) {
-               this.runOnUiThread(new Runnable() {
-                               public void run() {
-                                       for (AltosLaunchSite site : sites)
-                                               known_sites_adapter.add(site);
-                               }
-                       });
-       }
-
-       /* LocationProvider interface */
-
-       AltosLaunchSite current_location_site;
-
-       public void onLocationChanged(Location location) {
-               AltosDebug.debug("location changed");
-               if (current_location_site == null) {
-                       AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
-
-                       current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
-                       known_sites_adapter.insert(current_location_site, 0);
-
-                       if (selected_item != null)
-                               known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
-                       else {
-                               latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
-                               longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
-                       }
-               } else {
-                       current_location_site.latitude = location.getLatitude();
-                       current_location_site.longitude = location.getLongitude();
-               }
-       }
-
-       public void onStatusChanged(String provider, int status, Bundle extras) {
-       }
-
-       public void onProviderEnabled(String provider) {
-       }
-
-       public void onProviderDisabled(String provider) {
-       }
-
-       private double text(EditText view) throws ParseException {
-               return AltosParse.parse_double_locale(view.getEditableText().toString());
-       }
-
-       private double latitude() throws ParseException {
-               return text(latitude);
-       }
-
-       private double longitude() throws ParseException {
-               return text(longitude);
-       }
-
-       private int value(Spinner spinner) {
-               return (Integer) spinner.getSelectedItem();
-       }
-
-       private int min_z() {
-               return value(min_zoom);
-       }
-
-       private int max_z() {
-               return value(max_zoom);
-       }
-
-       private double value_distance(Spinner spinner) {
-               return (Double) spinner.getSelectedItem();
-       }
-
-       private double radius() {
-               double r = value_distance(radius);
-               if (AltosPreferences.imperial_units())
-                       r = AltosConvert.miles_to_meters(r);
-               else
-                       r = r * 1000;
-               return r;
-       }
-
-/*
-       private int bit(CheckBox box, int value) {
-               if (box.isChecked())
-                       return 1 << value;
-               return 0;
-       }
-*/
-
-       private int types() {
-/*
-               return (bit(hybrid, AltosMap.maptype_hybrid) |
-                       bit(satellite, AltosMap.maptype_satellite) |
-                       bit(roadmap, AltosMap.maptype_roadmap) |
-                       bit(terrain, AltosMap.maptype_terrain));
-*/
-               return 1 << AltosMap.maptype_hybrid;
-       }
-
-       private void load() {
-               if (loader != null)
-                       return;
-
-               try {
-                       double  lat = latitude();
-                       double  lon = longitude();
-                       int     min = min_z();
-                       int     max = max_z();
-                       double  r = radius();
-                       int     t = types();
-
-                       AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
-                                        lat, lon, min, max, r, t);
-                       loader = new AltosMapLoader(this, lat, lon, min, max, r, t, AltosMapOffline.scale);
-               } catch (ParseException e) {
-                       AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
-               }
-       }
-
-       private void add_numbers(Spinner spinner, int min, int max, int def) {
-
-               ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
-
-               int     spinner_def = 0;
-               int     pos = 0;
-
-               for (int i = min; i <= max; i++) {
-                       adapter.add(new Integer(i));
-                       if (i == def)
-                               spinner_def = pos;
-                       pos++;
-               }
-
-               spinner.setAdapter(adapter);
-               spinner.setSelection(spinner_def);
-       }
-
-
-       private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
-
-               ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
-
-               int     spinner_def = 0;
-               int     pos = 0;
-
-               double[] distances;
-               double  def;
-               if (AltosPreferences.imperial_units()) {
-                       distances = distances_mi;
-                       def = def_mi;
-               } else {
-                       distances = distances_km;
-                       def = def_km;
-               }
-
-               for (int i = 0; i < distances.length; i++) {
-                       adapter.add(distances[i]);
-                       if (distances[i] == def)
-                               spinner_def = pos;
-                       pos++;
-               }
-
-               spinner.setAdapter(adapter);
-               spinner.setSelection(spinner_def);
-       }
-
-
-
-       class SiteListListener implements OnItemSelectedListener {
-               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                       AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
-                       latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
-                       longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
-               }
-               public void onNothingSelected(AdapterView<?> parent) {
-               }
-
-               public SiteListListener() {
-               }
-       }
-
-       double[]        radius_mi = { 1, 2, 5, 10, 20 };
-       double          radius_def_mi = 2;
-       double[]        radius_km = { 1, 2, 5, 10, 20, 30 };
-       double          radius_def_km = 2;
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.map_preload);
-
-               // Set result CANCELED incase the user backs out
-               setResult(Activity.RESULT_CANCELED);
-
-               // Initialize the button to perform device discovery
-               Button loadButton = (Button) findViewById(R.id.preload_load);
-               loadButton.setOnClickListener(new OnClickListener() {
-                       public void onClick(View v) {
-                               load();
-                       }
-               });
-
-               latitude = (EditText) findViewById(R.id.preload_latitude);
-               longitude = (EditText) findViewById(R.id.preload_longitude);
-
-/*
-               hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
-               satellite = (CheckBox) findViewById(R.id.preload_satellite);
-               roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
-               terrain = (CheckBox) findViewById(R.id.preload_terrain);
-
-               hybrid.setChecked(true);
-*/
-
-               min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
-               add_numbers(min_zoom,
-                           AltosMap.min_zoom - AltosMap.default_zoom,
-                           AltosMap.max_zoom - AltosMap.default_zoom, -2);
-               max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
-               add_numbers(max_zoom,
-                           AltosMap.min_zoom - AltosMap.default_zoom,
-                           AltosMap.max_zoom - AltosMap.default_zoom, 2);
-               radius_label = (TextView) findViewById(R.id.preload_radius_label);
-               radius = (Spinner) findViewById(R.id.preload_radius);
-               if (AltosPreferences.imperial_units())
-                       radius_label.setText("Radius (miles)");
-               else
-                       radius_label.setText("Radius (km)");
-               add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
-
-               progress = (ProgressBar) findViewById(R.id.preload_progress);
-
-               // Initialize array adapters. One for already paired devices and
-               // one for newly discovered devices
-               known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
-
-               known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
-
-               known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
-               known_sites_spinner.setAdapter(known_sites_adapter);
-               known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
-
-               // Listen for GPS and Network position updates
-               LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
-
-               locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-
-               new AltosLaunchSites(this);
-       }
-
-       @Override
-       protected void onDestroy() {
-               super.onDestroy();
-
-               if (loader != null)
-                       loader.abort();
-
-               // Stop listening for location updates
-               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java
deleted file mode 100644 (file)
index bad0b82..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright © 2016 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.lang.ref.WeakReference;
-import java.util.*;
-import org.altusmetrum.AltosDroid.R;
-
-import android.app.Activity;
-import android.bluetooth.*;
-import android.content.*;
-import android.os.*;
-import android.view.*;
-import android.view.View.*;
-import android.widget.*;
-import android.widget.AdapterView.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class SetupActivity extends Activity {
-       private Spinner select_rate;
-       private Spinner set_units;
-       private Spinner map_type;
-       private Spinner map_source;
-       private Button manage_frequencies;
-       private Button preload_maps;
-       private Button done;
-
-       private boolean is_bound;
-       private Messenger service = null;
-
-       public final static String EXTRA_SETUP_CHANGES = "setup_changes";
-
-       private ServiceConnection connection = new ServiceConnection() {
-               public void onServiceConnected(ComponentName className, IBinder binder) {
-                       service = new Messenger(binder);
-               }
-
-               public void onServiceDisconnected(ComponentName className) {
-                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
-                       service = null;
-               }
-       };
-
-       void doBindService() {
-               bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
-               is_bound = true;
-       }
-
-       void doUnbindService() {
-               if (is_bound) {
-                       // If we have received the service, and hence registered with it, then now is the time to unregister.
-                       unbindService(connection);
-                       is_bound = false;
-               }
-       }
-
-       static final String[] rates = {
-               "38400",
-               "9600",
-               "2400",
-       };
-
-       static final String[] map_types = {
-               "Hybrid",
-               "Satellite",
-               "Roadmap",
-               "Terrain"
-       };
-
-       static final int[] map_type_values = {
-               AltosMap.maptype_hybrid,
-               AltosMap.maptype_satellite,
-               AltosMap.maptype_roadmap,
-               AltosMap.maptype_terrain,
-       };
-
-       static final String[] map_sources = {
-               "Online",
-               "Offline"
-       };
-
-       private int     set_telemetry_rate;
-       private int     set_map_source;
-       private int     set_map_type;
-       private boolean set_imperial_units;
-
-       private int     changes = 0;
-
-       private void add_change(int change) {
-               changes |= change;
-       }
-
-       private void done() {
-               Intent intent = new Intent();
-               if ((changes & AltosDroid.SETUP_BAUD) != 0)
-                       AltosPreferences.set_telemetry_rate(1, set_telemetry_rate);
-               if ((changes & AltosDroid.SETUP_UNITS) != 0)
-                       AltosPreferences.set_imperial_units(set_imperial_units);
-               if ((changes & AltosDroid.SETUP_MAP_SOURCE) != 0)
-                       AltosDroidPreferences.set_map_source(set_map_source);
-               if ((changes & AltosDroid.SETUP_MAP_TYPE) != 0)
-                       AltosPreferences.set_map_type(set_map_type);
-               intent.putExtra(EXTRA_SETUP_CHANGES, changes);
-               setResult(Activity.RESULT_OK, intent);
-               finish();
-       }
-
-       private void add_strings(Spinner spinner, String[] strings, int def) {
-               ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
-
-               for (int i = 0; i < strings.length; i++)
-                       adapter.add(strings[i]);
-
-               spinner.setAdapter(adapter);
-               if (def >= 0)
-                       spinner.setSelection(def);
-       }
-
-       private int default_rate_pos() {
-               int     default_rate = AltosPreferences.telemetry_rate(1);
-
-               for (int pos = 0; pos < rates.length; pos++) {
-                       if (string_to_rate(rates[pos]) == default_rate)
-                               return pos;
-               }
-               return -1;
-       }
-
-       private void setBaud(int baud) {
-               try {
-                       service.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
-                       set_telemetry_rate = baud;
-                       add_change(AltosDroid.SETUP_BAUD);
-               } catch (RemoteException e) {
-               }
-       }
-
-       private int string_to_rate(String baud) {
-               int     rate = AltosLib.ao_telemetry_rate_38400;
-               try {
-                       int     value = Integer.parseInt(baud);
-                       switch (value) {
-                       case 2400:
-                               rate = AltosLib.ao_telemetry_rate_2400;
-                               break;
-                       case 9600:
-                               rate = AltosLib.ao_telemetry_rate_9600;
-                               break;
-                       case 38400:
-                               rate = AltosLib.ao_telemetry_rate_38400;
-                               break;
-                       }
-               } catch (NumberFormatException e) {
-               }
-               return rate;
-       }
-
-       private void setBaud(String baud) {
-               setBaud(string_to_rate(baud));
-       }
-
-       private void select_rate(int pos) {
-               setBaud(rates[pos]);
-       }
-
-       static final String[] units = {
-               "Metric",
-               "Imperial"
-       };
-
-       private int default_units_pos() {
-               boolean imperial = AltosPreferences.imperial_units();
-
-               if (imperial)
-                       return 1;
-               return 0;
-       }
-
-       private void set_units(int pos) {
-               switch (pos) {
-               default:
-                       set_imperial_units = false;
-                       break;
-               case 1:
-                       set_imperial_units = true;
-                       break;
-               }
-               add_change(AltosDroid.SETUP_UNITS);
-       }
-
-       private int default_map_type_pos() {
-               int     default_map_type = AltosPreferences.map_type();
-
-               for (int pos = 0; pos < map_types.length; pos++)
-                       if (map_type_values[pos] == default_map_type)
-                               return pos;
-               return 0;
-       }
-
-       private void select_map_type(int pos) {
-               set_map_type = map_type_values[pos];
-               add_change(AltosDroid.SETUP_MAP_TYPE);
-       }
-
-       private int default_map_source_pos() {
-               int     default_source = AltosDroidPreferences.map_source();
-
-               switch (default_source) {
-               case AltosDroidPreferences.MAP_SOURCE_OFFLINE:
-                       return 1;
-               default:
-                       return 0;
-               }
-       }
-
-       private void select_map_source(int pos) {
-               switch (pos) {
-               default:
-                       set_map_source = AltosDroidPreferences.MAP_SOURCE_ONLINE;
-                       break;
-               case 1:
-                       set_map_source = AltosDroidPreferences.MAP_SOURCE_OFFLINE;
-                       break;
-               }
-               add_change(AltosDroid.SETUP_MAP_SOURCE);
-       }
-
-       private void manage_frequencies(){
-               Intent intent = new Intent(this, ManageFrequenciesActivity.class);
-               startActivity(intent);
-       }
-
-       private void preload_maps(){
-               Intent intent = new Intent(this, PreloadMapActivity.class);
-               startActivity(intent);
-       }
-
-       @Override
-       protected void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-
-               AltosDebug.init(this);
-               AltosDebug.debug("+++ ON CREATE +++");
-
-               // Initialise preferences
-               AltosDroidPreferences.init(this);
-
-               // Setup the window
-               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-               setContentView(R.layout.setup);
-
-               select_rate = (Spinner) findViewById(R.id.select_rate);
-               add_strings(select_rate, rates, default_rate_pos());
-               select_rate.setOnItemSelectedListener(new OnItemSelectedListener() {
-                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                                       select_rate(pos);
-                               }
-                               public void onNothingSelected(AdapterView<?> parent) {
-                               }
-                       });
-
-               set_units = (Spinner) findViewById(R.id.set_units);
-               add_strings(set_units, units, default_units_pos());
-               set_units.setOnItemSelectedListener(new OnItemSelectedListener() {
-                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                                       set_units(pos);
-                               }
-                               public void onNothingSelected(AdapterView<?> parent) {
-                               }
-                       });
-
-               map_type = (Spinner) findViewById(R.id.map_type);
-               add_strings(map_type, map_types, default_map_type_pos());
-               map_type.setOnItemSelectedListener(new OnItemSelectedListener() {
-                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                                       select_map_type(pos);
-                               }
-                               public void onNothingSelected(AdapterView<?> parent) {
-                               }
-                       });
-
-               map_source = (Spinner) findViewById(R.id.map_source);
-               add_strings(map_source, map_sources, default_map_source_pos());
-               map_source.setOnItemSelectedListener(new OnItemSelectedListener() {
-                               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                                       select_map_source(pos);
-                               }
-                               public void onNothingSelected(AdapterView<?> parent) {
-                               }
-                       });
-
-
-               manage_frequencies = (Button) findViewById(R.id.manage_frequencies);
-               manage_frequencies.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       manage_frequencies();
-                               }
-                       });
-
-               preload_maps = (Button) findViewById(R.id.preload_maps);
-               preload_maps.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       preload_maps();
-                               }
-                       });
-
-               done = (Button) findViewById(R.id.done);
-               done.setOnClickListener(new OnClickListener() {
-                               public void onClick(View v) {
-                                       done();
-                               }
-                       });
-
-               // Set result for when the user backs out
-               setResult(Activity.RESULT_CANCELED);
-       }
-
-       @Override
-       protected void onStart() {
-               super.onStart();
-               doBindService();
-       }
-
-       @Override
-       protected void onStop() {
-               super.onStop();
-               doUnbindService();
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java
deleted file mode 100644 (file)
index 8997d96..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.*;
-import android.widget.*;
-import android.location.Location;
-
-public class TabFlight extends AltosDroidTab {
-       private TextView speed_view;
-       private TextView height_view;
-       private TextView max_speed_view;
-       private TextView max_height_view;
-       private TextView elevation_view;
-       private TextView range_view;
-       private TextView bearing_view;
-       private TextView compass_view;
-       private TextView distance_view;
-       private TextView latitude_view;
-       private TextView longitude_view;
-       private View apogee_view;
-       private TextView apogee_voltage_view;
-       private TextView apogee_voltage_label;
-       private GoNoGoLights apogee_lights;
-       private View main_view;
-       private TextView main_voltage_view;
-       private TextView main_voltage_label;
-       private GoNoGoLights main_lights;
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_flight, container, false);
-
-               speed_view     = (TextView) v.findViewById(R.id.speed_value);
-               height_view    = (TextView) v.findViewById(R.id.height_value);
-               max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
-               max_height_view= (TextView) v.findViewById(R.id.max_height_value);
-               elevation_view = (TextView) v.findViewById(R.id.elevation_value);
-               range_view     = (TextView) v.findViewById(R.id.range_value);
-               bearing_view   = (TextView) v.findViewById(R.id.bearing_value);
-               compass_view   = (TextView) v.findViewById(R.id.compass_value);
-               distance_view  = (TextView) v.findViewById(R.id.distance_value);
-               latitude_view  = (TextView) v.findViewById(R.id.lat_value);
-               longitude_view = (TextView) v.findViewById(R.id.lon_value);
-
-               apogee_view = v.findViewById(R.id.apogee_view);
-               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
-               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
-                                                (ImageView) v.findViewById(R.id.apogee_greenled),
-                                                getResources());
-               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
-
-               main_view = v.findViewById(R.id.main_view);
-               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
-               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
-                                              (ImageView) v.findViewById(R.id.main_greenled),
-                                              getResources());
-               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
-
-               return v;
-       }
-
-       public String tab_name() { return AltosDroid.tab_flight_name; }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (state != null) {
-                       set_value(speed_view, AltosConvert.speed, 6, state.speed());
-                       set_value(height_view, AltosConvert.height, 6, state.height());
-                       set_value(max_speed_view, AltosConvert.speed, 6, state.max_speed());
-                       set_value(max_height_view, AltosConvert.height, 6, state.max_height());
-                       if (from_receiver != null) {
-                               elevation_view.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
-                               set_value(range_view, AltosConvert.distance, 6, from_receiver.range);
-                               bearing_view.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
-                               compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
-                               set_value(distance_view, AltosConvert.distance, 6, from_receiver.distance);
-                       } else { 
-                               elevation_view.setText("<unknown>");
-                               range_view.setText("<unknown>");
-                               bearing_view.setText("<unknown>");
-                               compass_view.setText("<unknown>");
-                               distance_view.setText("<unknown>");
-                       }
-                       if (state.gps != null) {
-                               latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                               longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-                       }
-
-                       if (state.apogee_voltage == AltosLib.MISSING) {
-                               apogee_view.setVisibility(View.GONE);
-                       } else {
-                               apogee_voltage_view.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
-                               apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
-                               apogee_view.setVisibility(View.VISIBLE);
-                       }
-
-                       if (state.main_voltage == AltosLib.MISSING) {
-                               main_view.setVisibility(View.GONE);
-                       } else {
-                               main_voltage_view.setText(AltosDroid.number("%4.2f V", state.main_voltage));
-                               main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
-                               main_view.setVisibility(View.VISIBLE);
-                       }
-               }
-       }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java
deleted file mode 100644 (file)
index a2f997f..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import java.io.*;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.graphics.*;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.view.*;
-import android.widget.*;
-import android.location.Location;
-import android.content.*;
-
-public class TabMap extends AltosDroidTab implements AltosDroidMapSourceListener {
-
-       AltosLatLon     here;
-
-       private TextView mDistanceView;
-       private TextView mBearingLabel;
-       private TextView mBearingView;
-       private TextView mTargetLatitudeView;
-       private TextView mTargetLongitudeView;
-       private TextView mReceiverLatitudeView;
-       private TextView mReceiverLongitudeView;
-       private AltosMapOffline map_offline;
-       private AltosMapOnline map_online;
-       private View view;
-       private int map_source;
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-       }
-
-       @Override
-       public void onCreate(Bundle savedInstanceState) {
-               super.onCreate(savedInstanceState);
-       }
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               view = inflater.inflate(R.layout.tab_map, container, false);
-               int map_source = AltosDroidPreferences.map_source();
-
-               mDistanceView  = (TextView)view.findViewById(R.id.distance_value);
-               mBearingLabel  = (TextView)view.findViewById(R.id.bearing_label);
-               mBearingView   = (TextView)view.findViewById(R.id.bearing_value);
-               mTargetLatitudeView  = (TextView)view.findViewById(R.id.target_lat_value);
-               mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
-               mReceiverLatitudeView  = (TextView)view.findViewById(R.id.receiver_lat_value);
-               mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
-               map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
-               map_offline.onCreateView(altos_droid);
-               map_online = new AltosMapOnline(view.getContext());
-               map_online.onCreateView(altos_droid);
-               map_source_changed(AltosDroidPreferences.map_source());
-               AltosDroidPreferences.register_map_source_listener(this);
-               return view;
-       }
-
-       @Override
-       public void onActivityCreated(Bundle savedInstanceState) {
-               super.onActivityCreated(savedInstanceState);
-               if (map_online != null)
-                       getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
-       }
-
-       @Override
-       public void onDestroyView() {
-               super.onDestroyView();
-               map_offline.onDestroyView();
-               map_online.onDestroyView();
-               AltosDroidPreferences.unregister_map_source_listener(this);
-       }
-
-       public String tab_name() { return AltosDroid.tab_map_name; }
-
-       private void center(double lat, double lon, double accuracy) {
-               if (map_offline != null)
-                       map_offline.center(lat, lon, accuracy);
-               if (map_online != null)
-                       map_online.center(lat, lon, accuracy);
-       }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (from_receiver != null) {
-                       String  direction = AltosDroid.direction(from_receiver, receiver);
-                       if (direction != null) {
-                               mBearingLabel.setText("Direction");
-                               mBearingView.setText(direction);
-                       } else {
-                               mBearingLabel.setText("Bearing");
-                               mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
-                       }
-                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
-               } else {
-                       mBearingLabel.setText("Bearing");
-                       mBearingView.setText("");
-                       set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
-               }
-
-               if (state != null) {
-                       if (state.gps != null) {
-                               mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                               mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-                       }
-               }
-
-               if (receiver != null) {
-                       double accuracy;
-
-                       here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
-                       if (receiver.hasAccuracy())
-                               accuracy = receiver.getAccuracy();
-                       else
-                               accuracy = 1000;
-                       mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
-                       mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
-                       center (here.lat, here.lon, accuracy);
-               }
-               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
-                       if (map_offline != null)
-                               map_offline.show(telem_state, state, from_receiver, receiver);
-               } else {
-                       if (map_online != null)
-                               map_online.show(telem_state, state, from_receiver, receiver);
-               }
-       }
-
-       public void map_source_changed(int map_source) {
-               this.map_source = map_source;
-               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
-                       if (map_online != null)
-                               map_online.set_visible(false);
-                       if (map_offline != null) {
-                               map_offline.set_visible(true);
-                               map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
-                       }
-               } else {
-                       if (map_offline != null)
-                               map_offline.set_visible(false);
-                       if (map_online != null) {
-                               map_online.set_visible(true);
-                               map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
-                       }
-               }
-       }
-
-       public TabMap() {
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java
deleted file mode 100644 (file)
index f317ae9..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.*;
-import android.widget.*;
-import android.location.Location;
-
-public class TabPad extends AltosDroidTab {
-       private TextView battery_voltage_view;
-       private GoNoGoLights battery_lights;
-
-       private TableRow receiver_row;
-       private TextView receiver_voltage_view;
-       private TextView receiver_voltage_label;
-       private GoNoGoLights receiver_voltage_lights;
-
-       private TableRow apogee_row;
-       private TextView apogee_voltage_view;
-       private TextView apogee_voltage_label;
-       private GoNoGoLights apogee_lights;
-
-       private TableRow main_row;
-       private TextView main_voltage_view;
-       private TextView main_voltage_label;
-       private GoNoGoLights main_lights;
-
-       private TextView data_logging_view;
-       private GoNoGoLights data_logging_lights;
-
-       private TextView gps_locked_view;
-       private GoNoGoLights gps_locked_lights;
-
-       private TextView gps_ready_view;
-       private GoNoGoLights gps_ready_lights;
-
-       private TextView receiver_latitude_view;
-       private TextView receiver_longitude_view;
-       private TextView receiver_altitude_view;
-
-       private TableRow[] ignite_row = new TableRow[4];
-       private TextView[] ignite_voltage_view = new TextView[4];
-       private TextView[] ignite_voltage_label = new TextView[4];
-       private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
-
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_pad, container, false);
-               battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
-               battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
-                                                 (ImageView) v.findViewById(R.id.battery_greenled),
-                                                 getResources());
-
-               receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
-               receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
-               receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
-               receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
-                                                          (ImageView) v.findViewById(R.id.receiver_greenled),
-                                                          getResources());
-
-               apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
-               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
-               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
-               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
-                                                (ImageView) v.findViewById(R.id.apogee_greenled),
-                                                getResources());
-
-               main_row = (TableRow) v.findViewById(R.id.main_row);
-               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
-               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
-               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
-                                              (ImageView) v.findViewById(R.id.main_greenled),
-                                              getResources());
-
-               data_logging_view = (TextView) v.findViewById(R.id.logging_value);
-               data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
-                                                     (ImageView) v.findViewById(R.id.logging_greenled),
-                                                     getResources());
-
-               gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
-               gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
-                                                   (ImageView) v.findViewById(R.id.gps_locked_greenled),
-                                                   getResources());
-
-               gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
-               gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
-                                                  (ImageView) v.findViewById(R.id.gps_ready_greenled),
-                                                  getResources());
-
-               for (int i = 0; i < 4; i++) {
-                       int row_id, view_id, label_id, lights_id;
-                       int red_id, green_id;
-                       switch (i) {
-                       case 0:
-                       default:
-                               row_id = R.id.ignite_a_row;
-                               view_id = R.id.ignite_a_voltage_value;
-                               label_id = R.id.ignite_a_voltage_label;
-                               red_id = R.id.ignite_a_redled;
-                               green_id = R.id.ignite_a_greenled;
-                               break;
-                       case 1:
-                               row_id = R.id.ignite_b_row;
-                               view_id = R.id.ignite_b_voltage_value;
-                               label_id = R.id.ignite_b_voltage_label;
-                               red_id = R.id.ignite_b_redled;
-                               green_id = R.id.ignite_b_greenled;
-                               break;
-                       case 2:
-                               row_id = R.id.ignite_c_row;
-                               view_id = R.id.ignite_c_voltage_value;
-                               label_id = R.id.ignite_c_voltage_label;
-                               red_id = R.id.ignite_c_redled;
-                               green_id = R.id.ignite_c_greenled;
-                               break;
-                       case 3:
-                               row_id = R.id.ignite_d_row;
-                               view_id = R.id.ignite_d_voltage_value;
-                               label_id = R.id.ignite_d_voltage_label;
-                               red_id = R.id.ignite_d_redled;
-                               green_id = R.id.ignite_d_greenled;
-                               break;
-                       }
-                       ignite_row[i] = (TableRow) v.findViewById(row_id);
-                       ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
-                       ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
-                       ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
-                                                            (ImageView) v.findViewById(green_id),
-                                                            getResources());
-               }
-
-               receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
-               receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
-               receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
-        return v;
-       }
-
-       public String tab_name() { return AltosDroid.tab_pad_name; }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (state != null) {
-                       battery_voltage_view.setText(AltosDroid.number(" %4.2f V", state.battery_voltage));
-                       battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
-                       if (state.apogee_voltage == AltosLib.MISSING) {
-                               apogee_row.setVisibility(View.GONE);
-                       } else {
-                               apogee_voltage_view.setText(AltosDroid.number(" %4.2f V", state.apogee_voltage));
-                               apogee_row.setVisibility(View.VISIBLE);
-                       }
-                       apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
-                       if (state.main_voltage == AltosLib.MISSING) {
-                               main_row.setVisibility(View.GONE);
-                       } else {
-                               main_voltage_view.setText(AltosDroid.number(" %4.2f V", state.main_voltage));
-                               main_row.setVisibility(View.VISIBLE);
-                       }
-                       main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
-
-                       int num_igniter = state.igniter_voltage == null ? 0 : state.igniter_voltage.length;
-
-                       for (int i = 0; i < 4; i++) {
-                               double voltage = i >= num_igniter ? AltosLib.MISSING : state.igniter_voltage[i];
-                               if (voltage == AltosLib.MISSING) {
-                                       ignite_row[i].setVisibility(View.GONE);
-                               } else {
-                                       ignite_voltage_view[i].setText(AltosDroid.number(" %4.2f V", voltage));
-                                       ignite_row[i].setVisibility(View.VISIBLE);
-                               }
-                               ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
-                       }
-
-                       if (state.cal_data().flight != 0) {
-                               if (state.state() <= AltosLib.ao_flight_pad)
-                                       data_logging_view.setText("Ready to record");
-                               else if (state.state() < AltosLib.ao_flight_landed)
-                                       data_logging_view.setText("Recording data");
-                               else
-                                       data_logging_view.setText("Recorded data");
-                       } else {
-                               data_logging_view.setText("Storage full");
-                       }
-                       data_logging_lights.set(state.cal_data().flight != 0, state.cal_data().flight == AltosLib.MISSING);
-
-                       if (state.gps != null) {
-                               int soln = state.gps.nsat;
-                               int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
-                               gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
-                               gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
-                               if (state.gps_ready)
-                                       gps_ready_view.setText("Ready");
-                               else
-                                       gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
-                       } else
-                               gps_locked_lights.set(false, true);
-                       gps_ready_lights.set(state.gps_ready, state.gps == null);
-               }
-
-               if (telem_state != null) {
-                       if (telem_state.receiver_battery == AltosLib.MISSING) {
-                               receiver_row.setVisibility(View.GONE);
-                       } else {
-                               receiver_voltage_view.setText(AltosDroid.number(" %4.2f V", telem_state.receiver_battery));
-                               receiver_row.setVisibility(View.VISIBLE);
-                       }
-                       receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
-               }
-
-               if (receiver != null) {
-                       double altitude = AltosLib.MISSING;
-                       if (receiver.hasAltitude())
-                               altitude = receiver.getAltitude();
-                       receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
-                       receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
-                       set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java
deleted file mode 100644 (file)
index 3df4838..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabRecover extends AltosDroidTab {
-       private TextView mBearingView;
-       private TextView mDirectionView;
-       private TextView mDistanceView;
-       private TextView mTargetLatitudeView;
-       private TextView mTargetLongitudeView;
-       private TextView mReceiverLatitudeView;
-       private TextView mReceiverLongitudeView;
-       private TextView mMaxHeightView;
-       private TextView mMaxSpeedView;
-       private TextView mMaxAccelView;
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_recover, container, false);
-
-               mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
-               mDirectionView = (TextView) v.findViewById(R.id.direction_value);
-               mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
-               mTargetLatitudeView  = (TextView) v.findViewById(R.id.target_lat_value);
-               mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
-               mReceiverLatitudeView  = (TextView) v.findViewById(R.id.receiver_lat_value);
-               mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
-               mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
-               mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
-               mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
-
-               return v;
-       }
-
-       public String tab_name() { return AltosDroid.tab_recover_name; }
-
-       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (from_receiver != null) {
-                       mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
-                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
-                       String direction = AltosDroid.direction(from_receiver, receiver);
-                       if (direction == null)
-                               mDirectionView.setText("");
-                       else
-                               mDirectionView.setText(direction);
-               }
-               if (state != null && state.gps != null) {
-                       mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                       mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-               }
-
-               if (receiver != null) {
-                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
-                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
-               }
-
-               if (state != null) {
-                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
-                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
-                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java
deleted file mode 100644 (file)
index b34a25b..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TabHost;
-import android.widget.TabWidget;
-
-/**
- * This is a helper class that implements the management of tabs and all
- * details of connecting a ViewPager with associated TabHost.  It relies on a
- * trick.  Normally a tab host has a simple API for supplying a View or
- * Intent that each tab will show.  This is not sufficient for switching
- * between pages.  So instead we make the content part of the tab host
- * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
- * view to show as the tab content.  It listens to changes in tabs, and takes
- * care of switch to the correct paged in the ViewPager whenever the selected
- * tab changes.
- */
-public class TabsAdapter extends FragmentPagerAdapter
-               implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
-       private final Context mContext;
-       private final TabHost mTabHost;
-       private final ViewPager mViewPager;
-       private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
-       private int position;
-
-       static class TabInfo {
-               private final String tag;
-               private final Class<?> clss;
-               private final Bundle args;
-               private Fragment fragment;
-
-               TabInfo(String _tag, Class<?> _class, Bundle _args) {
-                       tag = _tag;
-                       clss = _class;
-                       args = _args;
-               }
-       }
-
-       static class DummyTabFactory implements TabHost.TabContentFactory {
-               private final Context mContext;
-
-               public DummyTabFactory(Context context) {
-                       mContext = context;
-               }
-
-               public View createTabContent(String tag) {
-                       View v = new View(mContext);
-                       v.setMinimumWidth(0);
-                       v.setMinimumHeight(0);
-                       return v;
-               }
-       }
-
-       public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
-               super(activity.getSupportFragmentManager());
-               mContext = activity;
-               mTabHost = tabHost;
-               mViewPager = pager;
-               mTabHost.setOnTabChangedListener(this);
-               mViewPager.setAdapter(this);
-               mViewPager.setOnPageChangeListener(this);
-       }
-
-       public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
-               tabSpec.setContent(new DummyTabFactory(mContext));
-               String tag = tabSpec.getTag();
-
-               TabInfo info = new TabInfo(tag, clss, args);
-               mTabs.add(info);
-               mTabHost.addTab(tabSpec);
-               notifyDataSetChanged();
-       }
-
-       @Override
-       public int getCount() {
-               return mTabs.size();
-       }
-
-       @Override
-       public Fragment getItem(int position) {
-               TabInfo info = mTabs.get(position);
-               AltosDebug.debug("TabsAdapter.getItem(%d)", position);
-               info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
-               return info.fragment;
-       }
-
-       public Fragment currentItem() {
-               TabInfo info = mTabs.get(position);
-               return info.fragment;
-       }
-
-       public void onTabChanged(String tabId) {
-               AltosDroidTab   prev_frag = (AltosDroidTab) mTabs.get(position).fragment;
-
-               position = mTabHost.getCurrentTab();
-
-               AltosDroidTab   cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
-
-               if (prev_frag != cur_frag) {
-                       if (prev_frag != null) {
-                               prev_frag.set_visible(false);
-                       }
-               }
-               if (cur_frag != null) {
-                       cur_frag.set_visible(true);
-               }
-               AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position);
-               mViewPager.setCurrentItem(position);
-       }
-
-       public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-       }
-
-       public void onPageSelected(int position) {
-               // Unfortunately when TabHost changes the current tab, it kindly
-               // also takes care of putting focus on it when not in touch mode.
-               // The jerk.
-               // This hack tries to prevent this from pulling focus out of our
-               // ViewPager.
-               TabWidget widget = mTabHost.getTabWidget();
-               int oldFocusability = widget.getDescendantFocusability();
-               widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-               mTabHost.setCurrentTab(position);
-               widget.setDescendantFocusability(oldFocusability);
-       }
-
-       public void onPageScrollStateChanged(int state) {
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java
deleted file mode 100644 (file)
index 49ba547..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_13.*;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Environment;
-
-public class TelemetryLogger {
-       private Context   context = null;
-       private AltosLink link    = null;
-       private AltosLog  logger  = null;
-
-       private BroadcastReceiver mExternalStorageReceiver;
-
-       public TelemetryLogger(Context in_context, AltosLink in_link) {
-               context = in_context;
-               link    = in_link;
-
-               startWatchingExternalStorage();
-       }
-
-       public void stop() {
-               stopWatchingExternalStorage();
-               close();
-       }
-
-       private void close() {
-               if (logger != null) {
-                       AltosDebug.debug("Shutting down Telemetry Logging");
-                       logger.close();
-                       logger = null;
-               }
-       }
-
-       void handleExternalStorageState() {
-               String state = Environment.getExternalStorageState();
-               if (Environment.MEDIA_MOUNTED.equals(state)) {
-                       if (logger == null) {
-                               AltosDebug.debug("Starting up Telemetry Logging");
-                               logger = new AltosLog(link);
-                       }
-               } else {
-                       AltosDebug.debug("External Storage not present - stopping");
-                       close();
-               }
-       }
-
-       void startWatchingExternalStorage() {
-               mExternalStorageReceiver = new BroadcastReceiver() {
-                       @Override
-                       public void onReceive(Context context, Intent intent) {
-                               handleExternalStorageState();
-                       }
-               };
-               IntentFilter filter = new IntentFilter();
-               filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
-               filter.addAction(Intent.ACTION_MEDIA_REMOVED);
-               context.registerReceiver(mExternalStorageReceiver, filter);
-               handleExternalStorageState();
-       }
-
-       void stopWatchingExternalStorage() {
-               context.unregisterReceiver(mExternalStorageReceiver);
-       }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
deleted file mode 100644 (file)
index 5cfa647..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-
-package org.altusmetrum.AltosDroid;
-
-import java.text.*;
-import java.io.*;
-import java.util.*;
-import java.util.concurrent.*;
-import android.os.Handler;
-
-import org.altusmetrum.altoslib_13.*;
-
-
-public class TelemetryReader extends Thread {
-
-       int         crc_errors;
-
-       Handler     handler;
-
-       AltosLink   link;
-
-       LinkedBlockingQueue<AltosLine> telemQueue;
-
-       public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
-               AltosLine l = telemQueue.take();
-               if (l.line == null)
-                       throw new IOException("IO error");
-               AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
-               return telem;
-       }
-
-       public void close() {
-               link.remove_monitor(telemQueue);
-               link = null;
-               telemQueue.clear();
-               telemQueue = null;
-       }
-
-       public void run() {
-               try {
-                       AltosDebug.debug("starting loop");
-                       while (telemQueue != null) {
-                               try {
-                                       AltosTelemetry  telem = read();
-                                       telem.set_frequency(link.frequency);
-                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
-                               } catch (ParseException pp) {
-                                       AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
-                               } catch (AltosCRCException ce) {
-                                       ++crc_errors;
-                                       handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
-                               }
-                       }
-               } catch (InterruptedException ee) {
-               } catch (IOException ie) {
-                       AltosDebug.error("IO exception in telemetry reader");
-                       handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
-               } finally {
-                       close();
-               }
-       }
-
-       public TelemetryReader (AltosLink in_link, Handler in_handler) {
-               AltosDebug.debug("connected TelemetryReader create started");
-               link    = in_link;
-               handler = in_handler;
-
-               telemQueue = new LinkedBlockingQueue<AltosLine>();
-               link.add_monitor(telemQueue);
-               link.set_telemetry(AltosLib.ao_telemetry_standard);
-
-               AltosDebug.debug("connected TelemetryReader created");
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
deleted file mode 100644 (file)
index 22a2bbd..0000000
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.lang.ref.WeakReference;
-import java.util.concurrent.TimeoutException;
-import java.util.*;
-
-import android.app.Notification;
-//import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothAdapter;
-import android.hardware.usb.*;
-import android.content.Intent;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.Looper;
-import android.widget.Toast;
-import android.location.Criteria;
-
-import org.altusmetrum.altoslib_13.*;
-
-public class TelemetryService extends Service implements AltosIdleMonitorListener {
-
-       static final int MSG_REGISTER_CLIENT   = 1;
-       static final int MSG_UNREGISTER_CLIENT = 2;
-       static final int MSG_CONNECT           = 3;
-       static final int MSG_OPEN_USB          = 4;
-       static final int MSG_CONNECTED         = 5;
-       static final int MSG_CONNECT_FAILED    = 6;
-       static final int MSG_DISCONNECTED      = 7;
-       static final int MSG_TELEMETRY         = 8;
-       static final int MSG_SETFREQUENCY      = 9;
-       static final int MSG_CRC_ERROR         = 10;
-       static final int MSG_SETBAUD           = 11;
-       static final int MSG_DISCONNECT        = 12;
-       static final int MSG_DELETE_SERIAL     = 13;
-       static final int MSG_BLUETOOTH_ENABLED = 14;
-       static final int MSG_MONITOR_IDLE_START= 15;
-       static final int MSG_MONITOR_IDLE_STOP = 16;
-       static final int MSG_REBOOT            = 17;
-       static final int MSG_IGNITER_QUERY     = 18;
-       static final int MSG_IGNITER_FIRE      = 19;
-
-       // Unique Identification Number for the Notification.
-       // We use it on Notification start, and to cancel it.
-       private int NOTIFICATION = R.string.telemetry_service_label;
-       //private NotificationManager mNM;
-
-       ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
-       final Handler   handler   = new IncomingHandler(this);
-       final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.
-
-       // Name of the connected device
-       DeviceAddress address;
-       private AltosDroidLink  altos_link  = null;
-       private TelemetryReader telemetry_reader = null;
-       private TelemetryLogger telemetry_logger = null;
-
-       // Local Bluetooth adapter
-       private BluetoothAdapter bluetooth_adapter = null;
-
-       // Last data seen; send to UI when it starts
-       private TelemetryState  telemetry_state;
-
-       // Idle monitor if active
-       AltosIdleMonitor idle_monitor = null;
-
-       // Igniter bits
-       AltosIgnite ignite = null;
-       boolean ignite_running;
-
-       // Handler of incoming messages from clients.
-       static class IncomingHandler extends Handler {
-               private final WeakReference<TelemetryService> service;
-               IncomingHandler(TelemetryService s) { service = new WeakReference<TelemetryService>(s); }
-
-               @Override
-               public void handleMessage(Message msg) {
-                       DeviceAddress address;
-
-                       TelemetryService s = service.get();
-                       AltosDroidLink bt = null;
-                       if (s == null)
-                               return;
-
-                       switch (msg.what) {
-
-                               /* Messages from application */
-                       case MSG_REGISTER_CLIENT:
-                               s.add_client(msg.replyTo);
-                               break;
-                       case MSG_UNREGISTER_CLIENT:
-                               s.remove_client(msg.replyTo);
-                               break;
-                       case MSG_CONNECT:
-                               AltosDebug.debug("Connect command received");
-                               address = (DeviceAddress) msg.obj;
-                               AltosDroidPreferences.set_active_device(address);
-                               s.start_altos_bluetooth(address, false);
-                               break;
-                       case MSG_OPEN_USB:
-                               AltosDebug.debug("Open USB command received");
-                               UsbDevice device = (UsbDevice) msg.obj;
-                               s.start_usb(device);
-                               break;
-                       case MSG_DISCONNECT:
-                               AltosDebug.debug("Disconnect command received");
-                               s.address = null;
-                               if (!(Boolean) msg.obj)
-                                       AltosDroidPreferences.set_active_device(null);
-                               s.disconnect(true);
-                               break;
-                       case MSG_DELETE_SERIAL:
-                               AltosDebug.debug("Delete Serial command received");
-                               s.delete_serial((Integer) msg.obj);
-                               break;
-                       case MSG_SETFREQUENCY:
-                               AltosDebug.debug("MSG_SETFREQUENCY");
-                               s.telemetry_state.frequency = (Double) msg.obj;
-                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
-                                       try {
-                                               s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
-                                               s.altos_link.save_frequency();
-                                       } catch (InterruptedException e) {
-                                       } catch (TimeoutException e) {
-                                       }
-                               }
-                               s.send_to_clients();
-                               break;
-                       case MSG_SETBAUD:
-                               AltosDebug.debug("MSG_SETBAUD");
-                               s.telemetry_state.telemetry_rate = (Integer) msg.obj;
-                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
-                                       s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
-                                       s.altos_link.save_telemetry_rate();
-                               }
-                               s.send_to_clients();
-                               break;
-
-                               /*
-                                *Messages from AltosBluetooth
-                                */
-                       case MSG_CONNECTED:
-                               AltosDebug.debug("MSG_CONNECTED");
-                               bt = (AltosDroidLink) msg.obj;
-
-                               if (bt != s.altos_link) {
-                                       AltosDebug.debug("Stale message");
-                                       break;
-                               }
-                               AltosDebug.debug("Connected to device");
-                               try {
-                                       s.connected();
-                               } catch (InterruptedException ie) {
-                               }
-                               break;
-                       case MSG_CONNECT_FAILED:
-                               AltosDebug.debug("MSG_CONNECT_FAILED");
-                               bt = (AltosDroidLink) msg.obj;
-
-                               if (bt != s.altos_link) {
-                                       AltosDebug.debug("Stale message");
-                                       break;
-                               }
-                               if (s.address != null) {
-                                       AltosDebug.debug("Connection failed... retrying");
-                                       s.start_altos_bluetooth(s.address, true);
-                               } else {
-                                       s.disconnect(true);
-                               }
-                               break;
-                       case MSG_DISCONNECTED:
-
-                               /* This can be sent by either AltosDroidLink or TelemetryReader */
-                               AltosDebug.debug("MSG_DISCONNECTED");
-                               bt = (AltosDroidLink) msg.obj;
-
-                               if (bt != s.altos_link) {
-                                       AltosDebug.debug("Stale message");
-                                       break;
-                               }
-                               if (s.address != null) {
-                                       AltosDebug.debug("Connection lost... retrying");
-                                       s.start_altos_bluetooth(s.address, true);
-                               } else {
-                                       s.disconnect(true);
-                               }
-                               break;
-
-                               /*
-                                * Messages from TelemetryReader
-                                */
-                       case MSG_TELEMETRY:
-                               s.telemetry((AltosTelemetry) msg.obj);
-                               break;
-                       case MSG_CRC_ERROR:
-                               // forward crc error messages
-                               s.telemetry_state.crc_errors = (Integer) msg.obj;
-                               s.send_to_clients();
-                               break;
-                       case MSG_BLUETOOTH_ENABLED:
-                               AltosDebug.debug("TelemetryService notes that BT is now enabled");
-                               address = AltosDroidPreferences.active_device();
-                               if (address != null && !address.address.startsWith("USB"))
-                                       s.start_altos_bluetooth(address, false);
-                               break;
-                       case MSG_MONITOR_IDLE_START:
-                               AltosDebug.debug("start monitor idle");
-                               s.start_idle_monitor();
-                               break;
-                       case MSG_MONITOR_IDLE_STOP:
-                               AltosDebug.debug("stop monitor idle");
-                               s.stop_idle_monitor();
-                               break;
-                       case MSG_REBOOT:
-                               AltosDebug.debug("reboot");
-                               s.reboot_remote();
-                               break;
-                       case MSG_IGNITER_QUERY:
-                               AltosDebug.debug("igniter query");
-                               s.igniter_query(msg.replyTo);
-                               break;
-                       case MSG_IGNITER_FIRE:
-                               AltosDebug.debug("igniter fire");
-                               s.igniter_fire((String) msg.obj);
-                               break;
-                       default:
-                               super.handleMessage(msg);
-                       }
-               }
-       }
-
-       /* Handle telemetry packet
-        */
-       private void telemetry(AltosTelemetry telem) {
-               AltosState      state;
-
-               if (telemetry_state.states.containsKey(telem.serial()))
-                       state = telemetry_state.states.get(telem.serial());
-               else
-                       state = new AltosState(new AltosCalData());
-               telem.provide_data(state);
-               telemetry_state.states.put(telem.serial(), state);
-               telemetry_state.quiet = false;
-               if (state != null) {
-                       AltosPreferences.set_state(state,telem.serial());
-               }
-               send_to_clients();
-       }
-
-       /* Construct the message to deliver to clients
-        */
-       private Message message() {
-               if (telemetry_state == null)
-                       AltosDebug.debug("telemetry_state null!");
-               if (telemetry_state.states == null)
-                       AltosDebug.debug("telemetry_state.states null!");
-               return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
-       }
-
-       /* A new friend has connected
-        */
-       private void add_client(Messenger client) {
-
-               clients.add(client);
-               AltosDebug.debug("Client bound to service");
-
-               /* On connect, send the current state to the new client
-                */
-               send_to_client(client);
-               send_idle_mode_to_client(client);
-
-               /* If we've got an address from a previous session, then
-                * go ahead and try to reconnect to the device
-                */
-               if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
-                       AltosDebug.debug("Reconnecting now...");
-                       start_altos_bluetooth(address, false);
-               }
-       }
-
-       /* A client has disconnected, clean up
-        */
-       private void remove_client(Messenger client) {
-               clients.remove(client);
-               AltosDebug.debug("Client unbound from service");
-
-               /* When the list of clients is empty, stop the service if
-                * we have no current telemetry source
-                */
-
-                if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
-                        AltosDebug.debug("No clients, no connection. Stopping\n");
-                        stopSelf();
-                }
-       }
-
-       private void send_to_client(Messenger client) {
-               Message m = message();
-               try {
-                       client.send(m);
-               } catch (RemoteException e) {
-                       AltosDebug.error("Client %s disappeared", client.toString());
-                       remove_client(client);
-               }
-       }
-
-       private void send_to_clients() {
-               for (Messenger client : clients)
-                       send_to_client(client);
-       }
-
-       private void send_idle_mode_to_client(Messenger client) {
-               Message m = Message.obtain(null, AltosDroid.MSG_IDLE_MODE, idle_monitor != null);
-               try {
-                       client.send(m);
-               } catch (RemoteException e) {
-                       AltosDebug.error("Client %s disappeared", client.toString());
-                       remove_client(client);
-               }
-       }
-
-       private void send_idle_mode_to_clients() {
-               for (Messenger client : clients)
-                       send_idle_mode_to_client(client);
-       }
-
-       private void telemetry_start() {
-               if (telemetry_reader == null && idle_monitor == null && !ignite_running) {
-                       telemetry_reader = new TelemetryReader(altos_link, handler);
-                       telemetry_reader.start();
-               }
-       }
-
-       private void telemetry_stop() {
-               if (telemetry_reader != null) {
-                       AltosDebug.debug("disconnect(): stopping TelemetryReader");
-                       telemetry_reader.interrupt();
-                       try {
-                               telemetry_reader.join();
-                       } catch (InterruptedException e) {
-                       }
-                       telemetry_reader = null;
-               }
-       }
-
-       private void disconnect(boolean notify) {
-               AltosDebug.debug("disconnect(): begin");
-
-               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
-               telemetry_state.address = null;
-
-               if (idle_monitor != null)
-                       stop_idle_monitor();
-
-               if (altos_link != null)
-                       altos_link.closing();
-
-               stop_receiver_voltage_timer();
-
-               telemetry_stop();
-               if (telemetry_logger != null) {
-                       AltosDebug.debug("disconnect(): stopping TelemetryLogger");
-                       telemetry_logger.stop();
-                       telemetry_logger = null;
-               }
-               if (altos_link != null) {
-                       AltosDebug.debug("disconnect(): stopping AltosDroidLink");
-                       altos_link.close();
-                       altos_link = null;
-                       ignite = null;
-               }
-               telemetry_state.config = null;
-               if (notify) {
-                       AltosDebug.debug("disconnect(): send message to clients");
-                       send_to_clients();
-                       if (clients.isEmpty()) {
-                               AltosDebug.debug("disconnect(): no clients, terminating");
-                               stopSelf();
-                       }
-               }
-       }
-
-       private void start_usb(UsbDevice device) {
-               AltosUsb        d = new AltosUsb(this, device, handler);
-
-               if (d != null) {
-                       disconnect(false);
-                       altos_link = d;
-                       try {
-                               connected();
-                       } catch (InterruptedException ie) {
-                       }
-               }
-       }
-
-       private void delete_serial(int serial) {
-               telemetry_state.states.remove((Integer) serial);
-               AltosPreferences.remove_state(serial);
-               send_to_clients();
-       }
-
-       private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
-               if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled())
-                       return;
-
-               disconnect(false);
-
-               // Get the BluetoothDevice object
-               BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
-
-               this.address = address;
-               AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
-               altos_link = new AltosBluetooth(device, handler, pause);
-               telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
-               telemetry_state.address = address;
-               send_to_clients();
-       }
-
-       private void start_idle_monitor() {
-               if (altos_link != null && idle_monitor == null) {
-                       telemetry_stop();
-                       idle_monitor = new AltosIdleMonitor(this, altos_link, true, false);
-                       idle_monitor.set_callsign(AltosPreferences.callsign());
-                       idle_monitor.start();
-                       send_idle_mode_to_clients();
-               }
-       }
-
-       private void stop_idle_monitor() {
-               if (idle_monitor != null) {
-                       try {
-                               idle_monitor.abort();
-                       } catch (InterruptedException ie) {
-                       }
-                       idle_monitor = null;
-                       telemetry_start();
-                       send_idle_mode_to_clients();
-               }
-       }
-
-       private void reboot_remote() {
-               if (altos_link != null) {
-                       stop_idle_monitor();
-                       try {
-                               altos_link.start_remote();
-                               altos_link.printf("r eboot\n");
-                               altos_link.flush_output();
-                       } catch (TimeoutException te) {
-                       } catch (InterruptedException ie) {
-                       } finally {
-                               try {
-                                       altos_link.stop_remote();
-                               } catch (InterruptedException ie) {
-                               }
-                       }
-               }
-       }
-
-       private void ensure_ignite() {
-               if (ignite == null)
-                       ignite = new AltosIgnite(altos_link, true, false);
-       }
-
-       private synchronized void igniter_query(Messenger client) {
-               ensure_ignite();
-               HashMap<String,Integer> status_map = null;
-               ignite_running = true;
-               try {
-                       stop_idle_monitor();
-                       try {
-                               status_map = ignite.status();
-                       } catch (InterruptedException ie) {
-                               AltosDebug.debug("ignite.status interrupted");
-                       } catch (TimeoutException te) {
-                               AltosDebug.debug("ignite.status timeout");
-                       }
-               } finally {
-                       ignite_running = false;
-               }
-               Message m = Message.obtain(null, AltosDroid.MSG_IGNITER_STATUS, status_map);
-               try {
-                       client.send(m);
-               } catch (RemoteException e) {
-               }
-       }
-
-       private synchronized void igniter_fire(String igniter) {
-               ensure_ignite();
-               ignite_running = true;
-               stop_idle_monitor();
-               try {
-                       ignite.fire(igniter);
-               } catch (InterruptedException ie) {
-               } finally {
-                       ignite_running = false;
-               }
-       }
-
-       // Timer for receiver battery voltage monitoring
-       Timer receiver_voltage_timer;
-
-       private void update_receiver_voltage() {
-               if (altos_link != null && idle_monitor == null && !ignite_running) {
-                       try {
-                               double  voltage = altos_link.monitor_battery();
-                               telemetry_state.receiver_battery = voltage;
-                               send_to_clients();
-                       } catch (InterruptedException ie) {
-                       }
-               }
-       }
-
-       private void stop_receiver_voltage_timer() {
-               if (receiver_voltage_timer != null) {
-                       receiver_voltage_timer.cancel();
-                       receiver_voltage_timer.purge();
-                       receiver_voltage_timer = null;
-               }
-       }
-
-       private void start_receiver_voltage_timer() {
-               if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
-                       receiver_voltage_timer = new Timer();
-                       receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
-               }
-       }
-
-       private void connected() throws InterruptedException {
-               AltosDebug.debug("connected top");
-               AltosDebug.check_ui("connected\n");
-               try {
-                       if (altos_link == null)
-                               throw new InterruptedException("no bluetooth");
-                       telemetry_state.config = altos_link.config_data();
-                       altos_link.set_radio_frequency(telemetry_state.frequency);
-                       altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
-               } catch (TimeoutException e) {
-                       // If this timed out, then we really want to retry it, but
-                       // probably safer to just retry the connection from scratch.
-                       AltosDebug.debug("connected timeout");
-                       if (address != null) {
-                               AltosDebug.debug("connected timeout, retrying");
-                               start_altos_bluetooth(address, true);
-                       } else {
-                               handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
-                               disconnect(true);
-                       }
-                       return;
-               }
-
-               AltosDebug.debug("connected bluetooth configured");
-               telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
-               telemetry_state.address = address;
-
-               telemetry_start();
-
-               AltosDebug.debug("connected TelemetryReader started");
-
-               telemetry_logger = new TelemetryLogger(this, altos_link);
-
-               start_receiver_voltage_timer();
-
-               AltosDebug.debug("Notify UI of connection");
-
-               send_to_clients();
-       }
-
-
-       @Override
-       public void onCreate() {
-
-               AltosDebug.init(this);
-
-               // Initialise preferences
-               AltosDroidPreferences.init(this);
-
-               // Get local Bluetooth adapter
-               bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
-
-               telemetry_state = new TelemetryState();
-
-               // Create a reference to the NotificationManager so that we can update our notifcation text later
-               //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
-
-               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
-               telemetry_state.address = null;
-
-               /* Pull the saved state information out of the preferences database
-                */
-               ArrayList<Integer> serials = AltosPreferences.list_states();
-
-               telemetry_state.latest_serial = AltosPreferences.latest_state();
-
-               telemetry_state.quiet = true;
-
-               AltosDebug.debug("latest serial %d\n", telemetry_state.latest_serial);
-
-               for (int serial : serials) {
-                       AltosState saved_state = AltosPreferences.state(serial);
-                       if (saved_state != null) {
-                               if (telemetry_state.latest_serial == 0)
-                                       telemetry_state.latest_serial = serial;
-
-                               AltosDebug.debug("recovered old state serial %d flight %d",
-                                                serial,
-                                                saved_state.cal_data().flight);
-                               if (saved_state.gps != null)
-                                       AltosDebug.debug("\tposition %f,%f",
-                                                        saved_state.gps.lat,
-                                                        saved_state.gps.lon);
-                               telemetry_state.states.put(serial, saved_state);
-                       } else {
-                               AltosDebug.debug("Failed to recover state for %d", serial);
-                               AltosPreferences.remove_state(serial);
-                       }
-               }
-       }
-
-       @Override
-       public int onStartCommand(Intent intent, int flags, int startId) {
-               AltosDebug.debug("Received start id %d: %s", startId, intent);
-
-               CharSequence text = getText(R.string.telemetry_service_started);
-
-               // Create notification to be displayed while the service runs
-               Notification notification = new Notification(R.drawable.am_status_c, text, 0);
-
-               // The PendingIntent to launch our activity if the user selects this notification
-               PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
-                               new Intent(this, AltosDroid.class), 0);
-
-               // Set the info for the views that show in the notification panel.
-               notification.setLatestEventInfo(this, getText(R.string.telemetry_service_label), text, contentIntent);
-
-               // Set the notification to be in the "Ongoing" section.
-               notification.flags |= Notification.FLAG_ONGOING_EVENT;
-
-               // Move us into the foreground.
-               startForeground(NOTIFICATION, notification);
-
-               /* Start bluetooth if we don't have a connection already */
-               if (intent != null &&
-                   (telemetry_state.connect == TelemetryState.CONNECT_NONE ||
-                    telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
-               {
-                       String  action = intent.getAction();
-
-                       if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
-                               DeviceAddress address = AltosDroidPreferences.active_device();
-                               if (address != null && !address.address.startsWith("USB"))
-                                       start_altos_bluetooth(address, false);
-                       }
-               }
-
-               // We want this service to continue running until it is explicitly
-               // stopped, so return sticky.
-               return START_STICKY;
-       }
-
-       @Override
-       public void onDestroy() {
-
-               // Stop the bluetooth Comms threads
-               disconnect(true);
-
-               // Demote us from the foreground, and cancel the persistent notification.
-               stopForeground(true);
-
-               // Tell the user we stopped.
-               Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();
-       }
-
-       @Override
-       public IBinder onBind(Intent intent) {
-               return messenger.getBinder();
-       }
-
-       /* AltosIdleMonitorListener */
-       public void update(AltosState state, AltosListenerState listener_state) {
-               telemetry_state.states.put(state.cal_data().serial, state);
-               telemetry_state.receiver_battery = listener_state.battery;
-               send_to_clients();
-       }
-
-       public void failed() {
-       }
-
-       public void error(String reason) {
-               stop_idle_monitor();
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java
deleted file mode 100644 (file)
index 74d5cd2..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import java.util.*;
-import org.altusmetrum.altoslib_13.*;
-import android.location.Location;
-
-public class TelemetryState {
-       public static final int CONNECT_NONE         = 0;
-       public static final int CONNECT_DISCONNECTED = 1;
-       public static final int CONNECT_CONNECTING   = 2;
-       public static final int CONNECT_CONNECTED    = 3;
-
-       int             connect;
-       DeviceAddress   address;
-       AltosConfigData config;
-       int             crc_errors;
-       double          receiver_battery;
-       double          frequency;
-       int             telemetry_rate;
-
-       boolean         quiet;
-
-       HashMap<Integer,AltosState>     states;
-
-       int             latest_serial;
-
-       public TelemetryState() {
-               connect = CONNECT_NONE;
-               config = null;
-               states = new HashMap<Integer,AltosState>();
-               crc_errors = 0;
-               receiver_battery = AltosLib.MISSING;
-               frequency = AltosPreferences.frequency(0);
-               telemetry_rate = AltosPreferences.telemetry_rate(0);
-       }
-}