diff options
Diffstat (limited to 'altosdroid/src')
27 files changed, 1658 insertions, 238 deletions
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index baf38fb7..15efc0e2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -30,7 +30,7 @@ import android.bluetooth.BluetoothSocket; import android.os.Handler; //import android.os.Message; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class AltosBluetooth extends AltosDroidLink { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java index df348c9d..64980dc7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.io.*; import java.lang.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.graphics.*; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index b26a9bc8..026e836d 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -45,13 +45,15 @@ 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_9.*; +import org.altusmetrum.altoslib_10.*; -public class AltosDroid extends FragmentActivity implements AltosUnitsListener { +public class AltosDroid extends FragmentActivity implements AltosUnitsListener, LocationListener { // Actions sent to the telemetry server at startup time @@ -62,14 +64,26 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { 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_MAP_TYPE = 4; + public static final int REQUEST_IDLE_MODE = 5; + public static final int REQUEST_IGNITERS = 6; + public static final int REQUEST_SETUP = 7; - public int map_type = AltosMap.maptype_hybrid; + 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; @@ -95,8 +109,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { // field to display the version at the bottom of the screen private TextView mVersion; - private double frequency; - private int telemetry_rate; + private boolean idle_mode = false; + + public Location location = null; // Tabs TabHost mTabHost; @@ -142,6 +157,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { case MSG_UPDATE_AGE: ad.update_age(); break; + case MSG_IDLE_MODE: + ad.idle_mode = (Boolean) msg.obj; + ad.update_state(null); + break; } } }; @@ -212,8 +231,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { switch (telemetry_state.connect) { case TelemetryState.CONNECT_CONNECTED: if (telemetry_state.config != null) { - String str = String.format("S/N %d %6.3f MHz", telemetry_state.config.serial, - telemetry_state.frequency); + 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])); @@ -314,7 +333,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { state = newest_state; } - update_ui(telemetry_state, state, telemetry_state.location); + update_ui(telemetry_state, state); start_timer(); } @@ -379,7 +398,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } - void update_ui(TelemetryState telem_state, AltosState state, Location location) { + void update_ui(TelemetryState telem_state, AltosState state) { int prev_state = AltosLib.ao_flight_invalid; @@ -440,7 +459,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mCallsignView.setText(state.callsign); } if (saved_state == null || state.serial != saved_state.serial) { - mSerialView.setText(String.format("%d", state.serial)); + if (state.serial == AltosLib.MISSING) + mSerialView.setText(""); + else + mSerialView.setText(String.format("%d", state.serial)); } if (saved_state == null || state.flight != saved_state.flight) { if (state.flight == AltosLib.MISSING) @@ -457,7 +479,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } if (saved_state == null || state.rssi != saved_state.rssi) { - mRSSIView.setText(String.format("%d", state.rssi)); + if (state.rssi == AltosLib.MISSING) + mRSSIView.setText(""); + else + mRSSIView.setText(String.format("%d", state.rssi)); } } @@ -510,11 +535,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { return tab_view; } - public void set_map_source(int source) { - for (AltosDroidTab mTab : mTabs) - mTab.set_map_source(source); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -560,22 +580,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mAgeOldColor = getResources().getColor(R.color.old_color); } - private boolean ensureBluetooth() { + private void ensureBluetooth() { // Get local Bluetooth adapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - // If the adapter is null, then Bluetooth is not supported - if (mBluetoothAdapter == null) { - Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); - return false; - } - - if (!mBluetoothAdapter.isEnabled()) { + /* 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); } - - return true; } private boolean check_usb() { @@ -641,9 +654,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { return; } AltosDebug.debug("Starting by looking for bluetooth devices"); - if (ensureBluetooth()) - return; - finish(); + ensureBluetooth(); } } @@ -679,12 +690,27 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { 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, saved_state); } @Override public void onPause() { super.onPause(); AltosDebug.debug("- ON PAUSE -"); + // Stop listening for location updates + ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this); } @Override @@ -720,19 +746,46 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { 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.error("BT not enabled"); - stopService(new Intent(AltosDroid.this, TelemetryService.class)); - Toast.makeText(this, R.string.bt_not_enabled, Toast.LENGTH_SHORT).show(); - finish(); + AltosDebug.debug("BT not enabled"); } break; - case REQUEST_MAP_TYPE: + case REQUEST_IDLE_MODE: if (resultCode == Activity.RESULT_OK) - set_map_type(data); + 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) { @@ -749,6 +802,14 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + 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 { @@ -771,14 +832,37 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } - private void set_map_type(Intent data) { - int type = data.getIntExtra(MapTypeActivity.EXTRA_MAP_TYPE, -1); + private void idle_mode(Intent data) { + int type = data.getIntExtra(IdleModeActivity.EXTRA_IDLE_RESULT, -1); + Message msg; - AltosDebug.debug("intent set_map_type %d\n", type); - if (type != -1) { - map_type = type; - for (AltosDroidTab mTab : mTabs) - mTab.set_map_type(map_type); + 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; } } @@ -797,11 +881,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } - void setFrequency(String freq) { - try { - setFrequency (AltosParse.parse_double_net(freq.substring(11, 17))); - } catch (ParseException e) { - } + void setFrequency(AltosFrequency frequency) { + setFrequency (frequency.frequency); } void setBaud(int baud) { @@ -889,11 +970,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { Intent serverIntent = null; switch (item.getItemId()) { case R.id.connect_scan: - if (ensureBluetooth()) { - // Launch the DeviceListActivity to see devices and do scan - serverIntent = new Intent(this, DeviceListActivity.class); - startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); - } + 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 @@ -905,25 +985,21 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { disconnectDevice(); 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 String[] frequencies = { - "Channel 0 (434.550MHz)", - "Channel 1 (434.650MHz)", - "Channel 2 (434.750MHz)", - "Channel 3 (434.850MHz)", - "Channel 4 (434.950MHz)", - "Channel 5 (435.050MHz)", - "Channel 6 (435.150MHz)", - "Channel 7 (435.250MHz)", - "Channel 8 (435.350MHz)", - "Channel 9 (435.450MHz)" - }; + 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(frequencies, + builder_freq.setItems(frequency_strings, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { setFrequency(frequencies[item]); @@ -932,44 +1008,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { AlertDialog alert_freq = builder_freq.create(); alert_freq.show(); return true; - case R.id.select_rate: - // Set the TBT baud rate - - final String[] rates = { - "38400", - "9600", - "2400", - }; - - AlertDialog.Builder builder_rate = new AlertDialog.Builder(this); - builder_rate.setTitle("Pick a baud rate"); - builder_rate.setItems(rates, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int item) { - setBaud(rates[item]); - } - }); - AlertDialog alert_rate = builder_rate.create(); - alert_rate.show(); - return true; - case R.id.change_units: - boolean imperial = AltosPreferences.imperial_units(); - AltosPreferences.set_imperial_units(!imperial); - return true; - case R.id.preload_maps: - serverIntent = new Intent(this, PreloadMapActivity.class); - startActivityForResult(serverIntent, REQUEST_PRELOAD_MAPS); - return true; - case R.id.map_type: - serverIntent = new Intent(this, MapTypeActivity.class); - startActivityForResult(serverIntent, REQUEST_MAP_TYPE); - return true; - case R.id.map_source: - int source = AltosDroidPreferences.map_source(); - int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE; - AltosDroidPreferences.set_map_source(new_source); - set_map_source(new_source); - return true; case R.id.select_tracker: if (serials != null) { String[] trackers = new String[serials.length+1]; @@ -1010,12 +1048,17 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } 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) { + Location receiver) { if (from_receiver == null) return null; @@ -1044,4 +1087,24 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { 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, saved_state); + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + AltosDebug.debug("Location status now %d\n", status); + } + + public void onProviderEnabled(String provider) { + AltosDebug.debug("Location provider enabled %s\n", provider); + } + + public void onProviderDisabled(String provider) { + AltosDebug.debug("Location provider disabled %s\n", provider); + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java index 0e3511d3..0fd9af75 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java @@ -24,7 +24,7 @@ import java.util.UUID; import android.os.Handler; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public abstract class AltosDroidLink extends AltosLink { @@ -170,8 +170,8 @@ public abstract class AltosDroidLink extends AltosLink { } buffer_off = 0; } - if (AltosDebug.D) - debug_input(in_buffer[buffer_off]); +// if (AltosDebug.D) +// debug_input(in_buffer[buffer_off]); return in_buffer[buffer_off++]; } @@ -207,10 +207,9 @@ public abstract class AltosDroidLink extends AltosLink { public void print(String data) { byte[] bytes = data.getBytes(); - AltosDebug.debug("print(): begin"); +// AltosDebug.debug(data.replace('\n', '\\')); for (byte b : bytes) putchar(b); - AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'"); } public AltosDroidLink(Handler handler) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java index 59f08c34..43abef0f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java @@ -20,11 +20,13 @@ package org.altusmetrum.AltosDroid; import java.util.*; import java.io.*; import android.location.Location; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; 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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java new file mode 100644 index 00000000..e2775ef5 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapSourceListener.java @@ -0,0 +1,22 @@ +/* + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +public interface AltosDroidMapSourceListener { + public void map_source_changed(int map_source); +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 02defbcb..dd86c818 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -16,8 +16,12 @@ */ package org.altusmetrum.AltosDroid; +import java.io.*; +import java.util.*; +import java.text.*; + import android.content.Context; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class AltosDroidPreferences extends AltosPreferences { @@ -65,12 +69,19 @@ public class AltosDroidPreferences extends AltosPreferences { } } + 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() { @@ -78,4 +89,18 @@ public class AltosDroidPreferences extends AltosPreferences { return map_source; } } + + public static void register_map_source_listener(AltosDroidMapSourceListener l) { + synchronized(backend) { + if (map_source_listeners == null) + map_source_listeners = new LinkedList<AltosDroidMapSourceListener>(); + map_source_listeners.add(l); + } + } + + public static void unregister_map_source_listener(AltosDroidMapSourceListener l) { + synchronized(backend) { + map_source_listeners.remove(l); + } + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java index 14e6ce73..5c7ec513 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -24,9 +24,9 @@ import android.content.SharedPreferences; import android.os.Environment; import android.util.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; -public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { +public class AltosDroidPreferencesBackend extends AltosPreferencesBackend { public final static String NAME = "org.altusmetrum.AltosDroid"; private Context context = null; private SharedPreferences prefs = null; @@ -53,6 +53,8 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { } public AltosPreferencesBackend node(String key) { + if (!nodeExists(key)) + putBoolean(key, true); return new AltosDroidPreferencesBackend(context, key); } @@ -120,4 +122,8 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { public File homeDirectory() { return Environment.getExternalStorageDirectory(); } + + public void debug(String format, Object ... arguments) { + AltosDebug.debug(format, arguments); + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index 792d0621..77dbbcb1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.location.Location; import android.app.Activity; import android.graphics.Color; @@ -39,12 +39,6 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen public abstract String tab_name(); - public void set_map_type(int map_type) { - } - - public void set_map_source(int map_source) { - } - public void units_changed(boolean imperial_units) { if (!isHidden()) show(last_telem_state, last_state, last_from_receiver, last_receiver); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java index 28752110..ab142b17 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -20,7 +20,7 @@ package org.altusmetrum.AltosDroid; import java.util.*; import java.io.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.graphics.*; @@ -79,7 +79,7 @@ class Rocket implements Comparable { } } -public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface { +public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface, AltosMapTypeListener { ScaleGestureDetector scale_detector; boolean scaling; AltosMap map; @@ -133,7 +133,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA)) return; - AltosImage altos_image = cache.get(this, store, px_size, px_size); + AltosImage altos_image = this.get_image(); MapImage map_image = (MapImage) altos_image; @@ -150,8 +150,8 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal if (t.has_location()) { String message = null; switch (status) { - case AltosMapTile.loading: - message = "Loading..."; + case AltosMapTile.fetching: + message = "Fetching..."; break; case AltosMapTile.bad_request: message = "Internal error"; @@ -181,14 +181,14 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } } - public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - super(listener, upper_left, center, zoom, maptype, px_size, 2); + public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(cache, upper_left, center, zoom, maptype, px_size, 2); } } - public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - return new MapTile(listener, upper_left, center, zoom, maptype, px_size); + public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new MapTile(cache, upper_left, center, zoom, maptype, px_size); } public AltosMapPath new_path() { @@ -441,6 +441,8 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } 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) @@ -479,14 +481,20 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } } if (receiver != null) { - here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + 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); - map.set_maptype(altos_droid.map_type); + 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 */ @@ -504,7 +512,11 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal here_off_y = here_bitmap.getHeight() / 2; } - public void set_map_type(int map_type) { + 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); } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java index 10327091..fcdb930b 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -19,7 +19,7 @@ package org.altusmetrum.AltosDroid; import java.util.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import com.google.android.gms.maps.*; import com.google.android.gms.maps.model.*; @@ -102,7 +102,7 @@ class RocketOnline implements Comparable { } } -public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener { +public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, AltosMapTypeListener { public SupportMapFragment mMapFragment; private GoogleMap mMap; private boolean mapLoaded = false; @@ -124,7 +124,8 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke public void onCreateView(AltosDroid altos_droid) { this.altos_droid = altos_droid; - final int map_type = altos_droid.map_type; + final int map_type = AltosPreferences.map_type(); + AltosPreferences.register_map_type_listener(this); mMapFragment = new SupportMapFragment() { @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -144,9 +145,9 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke }; } -// public void onActivityCreated() { -// getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit(); -// } + public void onDestroyView() { + AltosPreferences.unregister_map_type_listener(this); + } private double pixel_distance(LatLng a, LatLng b) { Projection projection = mMap.getProjection(); @@ -190,7 +191,7 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke public void setupMap(int map_type) { mMap = mMapFragment.getMap(); if (mMap != null) { - set_map_type(map_type); + map_type_changed(map_type); mMap.setMyLocationEnabled(true); mMap.getUiSettings().setTiltGesturesEnabled(false); mMap.getUiSettings().setZoomControlsEnabled(false); @@ -281,7 +282,7 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke mPadMarker.setVisible(true); } } - if (state.gps != null) { + 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) @@ -308,7 +309,7 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke } - public void set_map_type(int map_type) { + public void map_type_changed(int map_type) { if (mMap != null) { if (map_type == AltosMap.maptype_hybrid) mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java index 12055dc5..4660512a 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java @@ -28,7 +28,7 @@ import android.hardware.usb.*; import android.app.*; import android.os.Handler; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class AltosUsb extends AltosDroidLink { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java index bdc80003..542ba1f5 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java @@ -22,7 +22,7 @@ import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; import android.location.Location; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class AltosVoice { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java new file mode 100644 index 00000000..ec12c192 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java @@ -0,0 +1,130 @@ +/* + * 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; version 2 of the License. + * + * 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_10.*; + +public class IdleModeActivity extends Activity { + private EditText callsign; + private Button connect; + private Button disconnect; + private Button reboot; + private Button igniters; + + public static final String EXTRA_IDLE_MODE = "idle_mode"; + public static final String EXTRA_IDLE_RESULT = "idle_result"; + + public static final int IDLE_MODE_CONNECT = 1; + public static final int IDLE_MODE_REBOOT = 2; + public static final int IDLE_MODE_IGNITERS = 3; + public static final int IDLE_MODE_DISCONNECT = 4; + + private void done(int type) { + AltosPreferences.set_callsign(callsign()); + Intent intent = new Intent(); + intent.putExtra(EXTRA_IDLE_RESULT, type); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + private String callsign() { + return callsign.getEditableText().toString(); + } + + public void connect_idle() { + done(IDLE_MODE_CONNECT); + } + + public void disconnect_idle() { + AltosDebug.debug("Disconnect idle button pressed"); + done(IDLE_MODE_DISCONNECT); + } + + public void reboot_idle() { + done(IDLE_MODE_REBOOT); + } + + public void igniters_idle() { + done(IDLE_MODE_IGNITERS); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.idle_mode); + + callsign = (EditText) findViewById(R.id.set_callsign); + callsign.setText(new StringBuffer(AltosPreferences.callsign())); + + connect = (Button) findViewById(R.id.connect_idle); + connect.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + connect_idle(); + } + }); + disconnect = (Button) findViewById(R.id.disconnect_idle); + disconnect.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + disconnect_idle(); + } + }); + + boolean idle_mode = getIntent().getBooleanExtra(AltosDroid.EXTRA_IDLE_MODE, false); + + if (idle_mode) + connect.setVisibility(View.GONE); + else + disconnect.setVisibility(View.GONE); + + reboot = (Button) findViewById(R.id.reboot_idle); + reboot.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + reboot_idle(); + } + }); + igniters = (Button) findViewById(R.id.igniters_idle); + igniters.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + igniters_idle(); + } + }); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java new file mode 100644 index 00000000..931c3cfd --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java @@ -0,0 +1,413 @@ +/* + * 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; version 2 of the License. + * + * 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_10.*; + +class IgniterItem { + public String name; + public String pretty; + public String status; + public LinearLayout igniter_view = null; + public TextView pretty_view = null; + public TextView status_view = null; + + private void update() { + if (pretty_view != null) + pretty_view.setText(pretty); + if (status_view != null) + status_view.setText(status); + } + + public void set(String name, String pretty, String status) { + if (!name.equals(this.name) || + !pretty.equals(this.pretty) || + !status.equals(this.status)) + { + this.name = name; + this.pretty = pretty; + this.status = status; + update(); + } + } + + public void realize(LinearLayout igniter_view, + TextView pretty_view, + TextView status_view) { + if (igniter_view != this.igniter_view || + pretty_view != this.pretty_view || + status_view != this.status_view) + { + this.igniter_view = igniter_view; + this.pretty_view = pretty_view; + this.status_view = status_view; + update(); + } + } + + public IgniterItem() { + } +} + +class IgniterAdapter extends ArrayAdapter<IgniterItem> { + int resource; + int selected_item = -1; + + public IgniterAdapter(Context context, int in_resource) { + super(context, in_resource); + resource = in_resource; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + IgniterItem item = getItem(position); + if (item.igniter_view == null) { + LinearLayout igniter_view = new LinearLayout(getContext()); + String inflater = Context.LAYOUT_INFLATER_SERVICE; + LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater); + li.inflate(resource, igniter_view, true); + + item.realize(igniter_view, + (TextView) igniter_view.findViewById(R.id.igniter_name), + (TextView) igniter_view.findViewById(R.id.igniter_status)); + } + if (position == selected_item) + item.igniter_view.setBackgroundColor(Color.RED); + else + item.igniter_view.setBackgroundColor(Color.BLACK); + return item.igniter_view; + } +} + +public class IgniterActivity extends Activity { + private ListView igniters_view; + private ToggleButton arm; + private Button fire; + + private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();; + + private IgniterAdapter igniters_adapter; + + private boolean is_bound; + private Messenger service = null; + private final Messenger messenger = new Messenger(new IncomingHandler(this)); + + private Timer query_timer; + private boolean query_timer_running; + + private Timer arm_timer; + private int arm_remaining; + + public static final int IGNITER_QUERY = 1; + public static final int IGNITER_FIRE = 2; + + // The Handler that gets information back from the Telemetry Service + static class IncomingHandler extends Handler { + private final WeakReference<IgniterActivity> igniter_activity; + IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); } + + @Override + public void handleMessage(Message msg) { + IgniterActivity ia = igniter_activity.get(); + + switch (msg.what) { + case AltosDroid.MSG_IGNITER_STATUS: + ia.igniter_status((HashMap <String,Integer>) msg.obj); + break; + } + } + }; + + + private ServiceConnection connection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder binder) { + service = new Messenger(binder); + query_timer_tick(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been unexpectedly disconnected - process crashed. + service = null; + } + }; + + void doBindService() { + bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE); + is_bound = true; + } + + void doUnbindService() { + if (is_bound) { + // If we have received the service, and hence registered with it, then now is the time to unregister. + unbindService(connection); + is_bound = false; + } + } + + private void done() { + Intent intent = new Intent(); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + class FireThread extends Thread { + private final String igniter; + + @Override + public void run() { + Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter); + try { + service.send(msg); + } catch (RemoteException re) { + } + } + + public FireThread(String igniter) { + this.igniter = igniter; + } + } + + private void fire_igniter() { + if (igniters_adapter.selected_item >= 0) { + IgniterItem item = igniters_adapter.getItem(igniters_adapter.selected_item); + FireThread ft = new FireThread(item.name); + ft.run(); + arm.setChecked(false); + } + } + + private void arm_igniter(boolean is_checked) { + if (is_checked) { + arm_timer_stop(); + arm_timer = new Timer(); + arm_remaining = 10; + arm_set_text(); + fire.setEnabled(true); + arm_timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + arm_timer_tick(); + }}, + 1000L, 1000L); + } else { + arm_timer_stop(); + fire.setEnabled(false); + } + } + + private synchronized void query_timer_tick() { + if (query_timer_running) + return; + if (service == null) + return; + query_timer_running = true; + Thread thread = new Thread(new Runnable() { + public void run() { + try { + Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY); + msg.replyTo = messenger; + if (service == null) { + synchronized(IgniterActivity.this) { + query_timer_running = false; + } + } else + service.send(msg); + } catch (RemoteException re) { + AltosDebug.debug("igniter query thread failed"); + synchronized(IgniterActivity.this) { + query_timer_running = false; + } + } + } + }); + thread.start(); + } + + private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) { + if (!status.containsKey(name)) + return false; + + IgniterItem item; + if (!igniters.containsKey(name)) { + item = new IgniterItem(); + igniters.put(name, item); + igniters_adapter.add(item); + } else + item = igniters.get(name); + + item.set(name, pretty, AltosIgnite.status_string(status.get(name))); + return true; + } + + private synchronized void igniter_status(HashMap <String,Integer> status) { + query_timer_running = false; + if (status == null) { + AltosDebug.debug("no igniter status"); + return; + } + set_igniter(status, "drogue", "Apogee"); + set_igniter(status, "main", "Main"); + for (int extra = 0;; extra++) { + String name = String.format("%d", extra); + String pretty = String.format("%c", 'A' + extra); + if (!set_igniter(status, name, pretty)) + break; + } + } + + private synchronized void arm_timer_stop() { + if (arm_timer != null) { + arm_timer.cancel(); + arm_timer = null; + } + arm_remaining = 0; + } + + private void arm_set_text() { + String text = String.format("Armed %d", arm_remaining); + + if (arm.isChecked()) + arm.setText(text); + arm.setTextOn(text); + } + + private void arm_timer_tick() { + --arm_remaining; + if (arm_remaining <= 0) { + arm_timer_stop(); + runOnUiThread(new Runnable() { + public void run() { + arm.setChecked(false); + fire.setEnabled(false); + } + }); + } else { + runOnUiThread(new Runnable() { + public void run() { + arm_set_text(); + } + }); + } + } + + private void select_item(int position) { + if (position != igniters_adapter.selected_item) { + if (igniters_adapter.selected_item >= 0) + igniters_view.setItemChecked(igniters_adapter.selected_item, false); + if (position >= 0) { + igniters_view.setItemChecked(position, true); + arm.setEnabled(true); + } else + arm.setEnabled(false); + igniters_adapter.selected_item = position; + } + } + + private class IgniterItemClickListener implements ListView.OnItemClickListener { + @Override + public void onItemClick(AdapterView<?> av, View v, int position, long id) { + AltosDebug.debug("select %d\n", position); + select_item(position); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.igniters); + + igniters_view = (ListView) findViewById(R.id.igniters); + igniters_view.setClickable(true); + + igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status); + + igniters_view.setAdapter(igniters_adapter); + igniters_view.setOnItemClickListener(new IgniterItemClickListener()); + + fire = (Button) findViewById(R.id.igniter_fire); + fire.setEnabled(false); + fire.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + fire_igniter(); + } + }); + + arm = (ToggleButton) findViewById(R.id.igniter_arm); + arm.setEnabled(false); + arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton v, boolean is_checked) { + arm_igniter(is_checked); + } + }); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } + + @Override + protected void onStart() { + super.onStart(); + doBindService(); + } + + @Override + protected void onResume() { + super.onResume(); + query_timer = new Timer(true); + query_timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + query_timer_tick(); + }}, + 0L, 5000L); + } + + @Override + protected void onPause() { + super.onPause(); + if (query_timer != null) { + query_timer.cancel(); + query_timer = null; + } + arm_timer_stop(); + arm.setChecked(false); + fire.setEnabled(false); + } + + @Override + protected void onStop() { + super.onStop(); + doUnbindService(); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java new file mode 100644 index 00000000..401cdc9d --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/ManageFrequenciesActivity.java @@ -0,0 +1,289 @@ +/* + * 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; version 2 of the License. + * + * 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_10.*; + +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() { + + 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 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); + + int pos = insert_item(new AltosFrequency(f, description_text)); + frequencies_adapter.selected_item = -1; + select_item(pos); + changed = true; + } catch (ParseException pe) { + } + hide_keyboard(); + } + + private void remove() { + if (frequencies_adapter.selected_item >= 0) { + frequencies_adapter.remove(frequencies_adapter.getItem(frequencies_adapter.selected_item)); + select_item(-1); + frequencies_view.setAdapter(frequencies_adapter); + changed = true; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.manage_frequencies); + + frequencies_view = (ListView) findViewById(R.id.frequencies); + frequencies_view.setClickable(true); + + frequencies_adapter = new FrequencyAdapter(this, R.layout.frequency); + + frequencies_view.setAdapter(frequencies_adapter); + frequencies_view.setOnItemClickListener(new FrequencyItemClickListener()); + + AltosFrequency[] frequencies = AltosPreferences.common_frequencies(); + for (AltosFrequency frequency : frequencies) + insert_item(frequency); + + set_frequency = (EditText) findViewById(R.id.set_frequency); + set_description = (EditText) findViewById(R.id.set_description); + + set = (Button) findViewById(R.id.set); + set.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + set(); + } + }); + + remove = (Button) findViewById(R.id.remove); + remove.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + remove(); + } + }); + + done = (Button) findViewById(R.id.done); + done.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + done(); + } + }); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } + + @Override + protected void onStart() { + super.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java index e1677ce6..cfbcdafc 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java @@ -34,7 +34,7 @@ import android.view.View.OnClickListener; import android.widget.*; import android.widget.AdapterView.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class MapTypeActivity extends Activity { private Button hybrid; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java index e6ce3809..13a44e1f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java @@ -41,7 +41,7 @@ import android.location.LocationManager; import android.location.LocationListener; import android.location.Criteria; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; /** * This Activity appears as a dialog. It lists any paired devices and @@ -107,7 +107,6 @@ public class PreloadMapActivity extends Activity implements AltosLaunchSiteListe } AltosMap map; - AltosMapLoader loader; class PreloadMapImage implements AltosImage { public void flush() { @@ -137,14 +136,14 @@ public class PreloadMapActivity extends Activity implements AltosLaunchSiteListe public void paint(AltosMapTransform t) { } - public PreloadMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - super(listener, upper_left, center, zoom, maptype, px_size, 2); + public PreloadMapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(cache, upper_left, center, zoom, maptype, px_size, 2); } } - public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - return new PreloadMapTile(listener, upper_left, center, zoom, maptype, px_size); + public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new PreloadMapTile(cache, upper_left, center, zoom, maptype, px_size); } public int width() { @@ -265,7 +264,7 @@ public class PreloadMapActivity extends Activity implements AltosLaunchSiteListe AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n", lat, lon, min, max, r, t); - loader.load(lat, lon, min, max, r, t); + new AltosMapLoader(map, this, lat, lon, min, max, r, t); } catch (ParseException e) { AltosDebug.debug("PreloadMap load raised exception %s", e.toString()); } @@ -398,8 +397,6 @@ public class PreloadMapActivity extends Activity implements AltosLaunchSiteListe map = new AltosMap(this); - loader = new AltosMapLoader(map, this); - // Listen for GPS and Network position updates LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java new file mode 100644 index 00000000..fdffc2b0 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/SetupActivity.java @@ -0,0 +1,339 @@ +/* + * 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; version 2 of the License. + * + * 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_10.*; + +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); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.setup); + + select_rate = (Spinner) findViewById(R.id.select_rate); + add_strings(select_rate, rates, default_rate_pos()); + select_rate.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + select_rate(pos); + } + public void onNothingSelected(AdapterView<?> parent) { + } + }); + + set_units = (Spinner) findViewById(R.id.set_units); + add_strings(set_units, units, default_units_pos()); + set_units.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + set_units(pos); + } + public void onNothingSelected(AdapterView<?> parent) { + } + }); + + map_type = (Spinner) findViewById(R.id.map_type); + add_strings(map_type, map_types, default_map_type_pos()); + map_type.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + select_map_type(pos); + } + public void onNothingSelected(AdapterView<?> parent) { + } + }); + + map_source = (Spinner) findViewById(R.id.map_source); + add_strings(map_source, map_sources, default_map_source_pos()); + map_source.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + select_map_source(pos); + } + public void onNothingSelected(AdapterView<?> parent) { + } + }); + + + manage_frequencies = (Button) findViewById(R.id.manage_frequencies); + manage_frequencies.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + manage_frequencies(); + } + }); + + preload_maps = (Button) findViewById(R.id.preload_maps); + preload_maps.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + preload_maps(); + } + }); + + done = (Button) findViewById(R.id.done); + done.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + done(); + } + }); + + // Set result for when the user backs out + setResult(Activity.RESULT_CANCELED); + } + + @Override + protected void onStart() { + super.onStart(); + doBindService(); + } + + @Override + protected void onStop() { + super.onStop(); + doUnbindService(); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java index 095d6b33..9bbdc060 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.os.Bundle; @@ -87,7 +87,7 @@ public class TabFlight extends AltosDroidTab { 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.speed, 6, state.max_height()); + 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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 3c236d58..19ce86c9 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -20,7 +20,7 @@ package org.altusmetrum.AltosDroid; import java.util.*; import java.io.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.graphics.*; @@ -32,7 +32,7 @@ import android.widget.*; import android.location.Location; import android.content.*; -public class TabMap extends AltosDroidTab { +public class TabMap extends AltosDroidTab implements AltosDroidMapSourceListener { AltosLatLon here; @@ -74,7 +74,8 @@ public class TabMap extends AltosDroidTab { map_offline.onCreateView(altos_droid); map_online = new AltosMapOnline(view.getContext()); map_online.onCreateView(altos_droid); - set_map_source(AltosDroidPreferences.map_source()); + map_source_changed(AltosDroidPreferences.map_source()); + AltosDroidPreferences.register_map_source_listener(this); return view; } @@ -88,6 +89,9 @@ public class TabMap extends AltosDroidTab { @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; } @@ -144,16 +148,7 @@ public class TabMap extends AltosDroidTab { } } - @Override - public void set_map_type(int map_type) { - if (map_offline != null) - map_offline.set_map_type(map_type); - if (map_online != null) - map_online.set_map_type(map_type); - } - - @Override - public void set_map_source(int map_source) { + public void map_source_changed(int map_source) { this.map_source = map_source; if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) { if (map_online != null) diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index f5fbaf61..1194eb00 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.os.Bundle; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java index ee82d391..8742227b 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.app.Activity; import android.os.Bundle; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java index 6f595817..bdea0986 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java @@ -1,6 +1,6 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 473a4bfb..0ac6bb5c 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -25,7 +25,7 @@ import java.util.*; import java.util.concurrent.*; import android.os.Handler; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; public class TelemetryReader extends Thread { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 92a7ecfa..3c1a1782 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -38,14 +38,11 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.Looper; import android.widget.Toast; -import android.location.Location; -import android.location.LocationManager; -import android.location.LocationListener; import android.location.Criteria; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; -public class TelemetryService extends Service implements LocationListener { +public class TelemetryService extends Service implements AltosIdleMonitorListener { static final int MSG_REGISTER_CLIENT = 1; static final int MSG_UNREGISTER_CLIENT = 2; @@ -60,6 +57,12 @@ public class TelemetryService extends Service implements LocationListener { 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. @@ -82,6 +85,13 @@ public class TelemetryService extends Service implements LocationListener { // 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; @@ -89,10 +99,13 @@ public class TelemetryService extends Service implements LocationListener { @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 */ @@ -104,7 +117,7 @@ public class TelemetryService extends Service implements LocationListener { break; case MSG_CONNECT: AltosDebug.debug("Connect command received"); - DeviceAddress address = (DeviceAddress) msg.obj; + address = (DeviceAddress) msg.obj; AltosDroidPreferences.set_active_device(address); s.start_altos_bluetooth(address, false); break; @@ -206,6 +219,32 @@ public class TelemetryService extends Service implements LocationListener { 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); } @@ -224,7 +263,7 @@ public class TelemetryService extends Service implements LocationListener { telem.update_state(state); telemetry_state.states.put(telem.serial, state); if (state != null) { - AltosPreferences.set_state(telem.serial, state, null); + AltosPreferences.set_state(state); } send_to_clients(); } @@ -248,7 +287,8 @@ public class TelemetryService extends Service implements LocationListener { /* On connect, send the current state to the new client */ - send_to_client(client, message()); + 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 @@ -275,7 +315,8 @@ public class TelemetryService extends Service implements LocationListener { } } - private void send_to_client(Messenger client, Message m) { + private void send_to_client(Messenger client) { + Message m = message(); try { client.send(m); } catch (RemoteException e) { @@ -285,22 +326,33 @@ public class TelemetryService extends Service implements LocationListener { } private void send_to_clients() { - Message m = message(); for (Messenger client : clients) - send_to_client(client, m); + send_to_client(client); } - private void disconnect(boolean notify) { - AltosDebug.debug("disconnect(): begin"); - - telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; - telemetry_state.address = null; + 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); + } + } - if (altos_link != null) - altos_link.closing(); + private void send_idle_mode_to_clients() { + for (Messenger client : clients) + send_idle_mode_to_client(client); + } - stop_receiver_voltage_timer(); + 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(); @@ -310,6 +362,23 @@ public class TelemetryService extends Service implements LocationListener { } 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(); @@ -319,6 +388,7 @@ public class TelemetryService extends Service implements LocationListener { AltosDebug.debug("disconnect(): stopping AltosDroidLink"); altos_link.close(); altos_link = null; + ignite = null; } telemetry_state.config = null; if (notify) { @@ -351,10 +421,14 @@ public class TelemetryService extends Service implements LocationListener { } private void start_altos_bluetooth(DeviceAddress address, boolean pause) { - // Get the BLuetoothDevice object - BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address); + 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); @@ -363,14 +437,95 @@ public class TelemetryService extends Service implements LocationListener { 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) { + 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) { } } @@ -418,8 +573,7 @@ public class TelemetryService extends Service implements LocationListener { telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; telemetry_state.address = address; - telemetry_reader = new TelemetryReader(altos_link, handler); - telemetry_reader.start(); + telemetry_start(); AltosDebug.debug("connected TelemetryReader started"); @@ -444,11 +598,6 @@ public class TelemetryService extends Service implements LocationListener { // Get local Bluetooth adapter bluetooth_adapter = BluetoothAdapter.getDefaultAdapter(); - // If the adapter is null, then Bluetooth is not supported - if (bluetooth_adapter == null) { - Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); - } - telemetry_state = new TelemetryState(); // Create a reference to the NotificationManager so that we can update our notifcation text later @@ -464,31 +613,23 @@ public class TelemetryService extends Service implements LocationListener { telemetry_state.latest_serial = AltosPreferences.latest_state(); for (int serial : serials) { - AltosSavedState saved_state = AltosPreferences.state(serial); + AltosState saved_state = AltosPreferences.state(serial); if (saved_state != null) { - if (serial == 0) { - serial = saved_state.state.serial; - AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state); - AltosPreferences.remove_state(0); - } if (telemetry_state.latest_serial == 0) telemetry_state.latest_serial = serial; - AltosDebug.debug("recovered old state serial %d flight %d\n", + AltosDebug.debug("recovered old state serial %d flight %d", serial, - saved_state.state.flight); - if (saved_state.state.gps != null) - AltosDebug.debug("\tposition %f,%f\n", - saved_state.state.gps.lat, - saved_state.state.gps.lon); - telemetry_state.states.put(serial, saved_state.state); + saved_state.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); } } - - // Listen for GPS and Network position updates - LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); - - locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this); } @Override @@ -535,9 +676,6 @@ public class TelemetryService extends Service implements LocationListener { @Override public void onDestroy() { - // Stop listening for location updates - ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this); - // Stop the bluetooth Comms threads disconnect(true); @@ -553,20 +691,17 @@ public class TelemetryService extends Service implements LocationListener { return messenger.getBinder(); } - - public void onLocationChanged(Location location) { - telemetry_state.location = location; - AltosDebug.debug("location changed"); + /* AltosIdleMonitorListener */ + public void update(AltosState state, AltosListenerState listener_state) { + telemetry_state.states.put(state.serial, state); + telemetry_state.receiver_battery = listener_state.battery; send_to_clients(); } - public void onStatusChanged(String provider, int status, Bundle extras) { + public void failed() { } - public void onProviderEnabled(String provider) { + public void error(String reason) { + stop_idle_monitor(); } - - public void onProviderDisabled(String provider) { - } - } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index c81dfcd2..d3ccf0a9 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -18,7 +18,7 @@ package org.altusmetrum.AltosDroid; import java.util.*; -import org.altusmetrum.altoslib_9.*; +import org.altusmetrum.altoslib_10.*; import android.location.Location; public class TelemetryState { @@ -30,7 +30,6 @@ public class TelemetryState { int connect; DeviceAddress address; AltosConfigData config; - Location location; int crc_errors; double receiver_battery; double frequency; @@ -44,7 +43,6 @@ public class TelemetryState { connect = CONNECT_NONE; config = null; states = new HashMap<Integer,AltosState>(); - location = null; crc_errors = 0; receiver_battery = AltosLib.MISSING; frequency = AltosPreferences.frequency(0); |