+++ /dev/null
-<?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>
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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() { }
+
+}
--- /dev/null
+/*
+ * 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");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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() { }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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@";
+}
+
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+ };
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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() {
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+ }
+}
--- /dev/null
+/*
+ * 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) {
+ }
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <style name="CustomTheme" parent="android:Theme.Holo">
+ </style>
+</resources>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <usb-device vendor-id="65534" />
+ <usb-device vendor-id="1027" />
+</resources>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<resources>
- <style name="CustomTheme" parent="android:Theme.Holo">
- </style>
-</resources>
+++ /dev/null
-<?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>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<resources>
- <usb-device vendor-id="65534" />
- <usb-device vendor-id="1027" />
-</resources>
+++ /dev/null
-/*
- * 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() { }
-
-}
+++ /dev/null
-/*
- * 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");
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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() { }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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@";
-}
-
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
- }
- };
-
-}
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * 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() {
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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());
- }
- }
-}
+++ /dev/null
-/*
- * 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) {
- }
-}
+++ /dev/null
-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);
- }
-
-}
+++ /dev/null
-/*
- * 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");
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}