diff options
Diffstat (limited to 'altosdroid/src')
15 files changed, 625 insertions, 310 deletions
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index 484efaf8..51ef5e94 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -52,6 +52,7 @@ public class AltosBluetooth extends AltosLink { // Constructor public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) { +// set_debug(D); adapter = BluetoothAdapter.getDefaultAdapter(); device = in_device; handler = in_handler; @@ -136,9 +137,27 @@ public class AltosBluetooth extends AltosLink { } } + 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); + } + private synchronized void wait_connected() throws InterruptedException, IOException { if (input == null) { + if (D) Log.d(TAG, "wait_connected..."); wait(); + if (D) Log.d(TAG, "wait_connected done"); if (input == null) throw new IOException(); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index c9c38d98..f6cceac9 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -44,32 +44,34 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.Window; +import android.view.View; import android.widget.TabHost; import android.widget.TextView; +import android.widget.RelativeLayout; import android.widget.Toast; import android.app.AlertDialog; import android.location.Location; import org.altusmetrum.altoslib_5.*; -public class AltosDroid extends FragmentActivity { +public class AltosDroid extends FragmentActivity implements AltosUnitsListener { // Debugging static final String TAG = "AltosDroid"; static final boolean D = true; // Message types received by our Handler - public static final int MSG_STATE_CHANGE = 1; - public static final int MSG_TELEMETRY = 2; - public static final int MSG_UPDATE_AGE = 3; - public static final int MSG_LOCATION = 4; - public static final int MSG_CRC_ERROR = 5; + + public static final int MSG_STATE = 1; + public static final int MSG_UPDATE_AGE = 2; // Intent request codes - private static final int REQUEST_CONNECT_DEVICE = 1; - private static final int REQUEST_ENABLE_BT = 2; + public static final int REQUEST_CONNECT_DEVICE = 1; + public static final int REQUEST_ENABLE_BT = 2; public static FragmentManager fm; + private BluetoothAdapter mBluetoothAdapter = null; + // Layout Views private TextView mTitle; @@ -78,12 +80,16 @@ public class AltosDroid extends FragmentActivity { private TextView mRSSIView; private TextView mSerialView; private TextView mFlightView; + private RelativeLayout mStateLayout; private TextView mStateView; private TextView mAgeView; // field to display the version at the bottom of the screen private TextView mVersion; + private double frequency; + private int telemetry_rate; + // Tabs TabHost mTabHost; AltosViewPager mViewPager; @@ -92,23 +98,14 @@ public class AltosDroid extends FragmentActivity { int tabHeight; // Timer and Saved flight state for Age calculation - private Timer timer = new Timer(); + private Timer timer; AltosState saved_state; - Location saved_location; // Service private boolean mIsBound = false; private Messenger mService = null; final Messenger mMessenger = new Messenger(new IncomingHandler(this)); - // Preferences - private AltosDroidPreferences prefs = null; - - // TeleBT Config data - private AltosConfigData mConfigData = null; - // Local Bluetooth adapter - private BluetoothAdapter mBluetoothAdapter = null; - // Text to Speech private AltosVoice mAltosVoice = null; @@ -120,39 +117,21 @@ public class AltosDroid extends FragmentActivity { @Override public void handleMessage(Message msg) { AltosDroid ad = mAltosDroid.get(); + switch (msg.what) { - case MSG_STATE_CHANGE: - if(D) Log.d(TAG, "MSG_STATE_CHANGE: " + msg.arg1); - switch (msg.arg1) { - case TelemetryService.STATE_CONNECTED: - ad.mConfigData = (AltosConfigData) msg.obj; - String str = String.format(" %s S/N: %d", ad.mConfigData.product, ad.mConfigData.serial); - ad.mTitle.setText(R.string.title_connected_to); - ad.mTitle.append(str); - Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show(); - break; - case TelemetryService.STATE_CONNECTING: - ad.mTitle.setText(R.string.title_connecting); - break; - case TelemetryService.STATE_READY: - case TelemetryService.STATE_NONE: - ad.mConfigData = null; - ad.mTitle.setText(R.string.title_not_connected); - break; + case MSG_STATE: + if(D) Log.d(TAG, "MSG_STATE"); + TelemetryState telemetry_state = (TelemetryState) msg.obj; + if (telemetry_state == null) { + Log.d(TAG, "telemetry_state null!"); + return; } - break; - case MSG_TELEMETRY: - ad.update_ui((AltosState) msg.obj); - break; - case MSG_LOCATION: - ad.set_location((Location) msg.obj); - break; - case MSG_CRC_ERROR: + + ad.update_state(telemetry_state); break; case MSG_UPDATE_AGE: - if (ad.saved_state != null) { - ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.received_time + 500) / 1000)); - } + if(D) Log.d(TAG, "MSG_UPDATE_AGE"); + ad.update_age(); break; } } @@ -208,10 +187,65 @@ public class AltosDroid extends FragmentActivity { mTabs.remove(mTab); } - void set_location(Location location) { - saved_location = location; - Log.d(TAG, "set_location"); - update_ui(saved_state); + 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", telemetry_state.config.serial, + telemetry_state.frequency); + 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])); + mTitle.setText(str); + } else { + mTitle.setText(R.string.title_connected_to); + } + break; + case TelemetryState.CONNECT_CONNECTING: + mTitle.setText(R.string.title_connecting); + break; + case TelemetryState.CONNECT_READY: + case TelemetryState.CONNECT_NONE: + mTitle.setText(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; + } + } + + boolean registered_units_listener; + + void update_state(TelemetryState telemetry_state) { + + if (!registered_units_listener) { + registered_units_listener = true; + AltosPreferences.register_units_listener(this); + } + + update_title(telemetry_state); + update_ui(telemetry_state.state, telemetry_state.location); + if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) + start_timer(); + else + stop_timer(); } boolean same_string(String a, String b) { @@ -226,42 +260,73 @@ public class AltosDroid extends FragmentActivity { } } - void update_ui(AltosState state) { + void update_age() { + if (saved_state != null) + mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000)); + } + + void update_ui(AltosState state, Location location) { Log.d(TAG, "update_ui"); - if (state != null && saved_state != null) { - if (saved_state.state != state.state) { - String currentTab = mTabHost.getCurrentTabTag(); - Log.d(TAG, "switch state"); - switch (state.state) { - case AltosLib.ao_flight_boost: - if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent"); - break; - case AltosLib.ao_flight_drogue: - if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent"); - break; - case AltosLib.ao_flight_landed: - if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed"); - break; - } - } - } + + int prev_state = AltosLib.ao_flight_invalid; AltosGreatCircle from_receiver = null; - if (state != null && saved_location != null && state.gps != null && state.gps.locked) { - double altitude = 0; - if (saved_location.hasAltitude()) - altitude = saved_location.getAltitude(); - from_receiver = new AltosGreatCircle(saved_location.getLatitude(), - saved_location.getLongitude(), - altitude, - state.gps.lat, - state.gps.lon, - state.gps.alt); - } + if (saved_state != null) + prev_state = saved_state.state; if (state != null) { + Log.d(TAG, String.format("prev state %d new state %d\n", prev_state, state.state)); + 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 && saved_state.gps != null) + prev_locked = saved_state.gps.locked; + if (prev_locked != locked) { + String currentTab = mTabHost.getCurrentTabTag(); + if (locked) { + if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent"); + } else { + if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad"); + } + } + } else { + if (prev_state != state.state) { + String currentTab = mTabHost.getCurrentTabTag(); + Log.d(TAG, "switch state"); + switch (state.state) { + case AltosLib.ao_flight_boost: + if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent"); + break; + case AltosLib.ao_flight_drogue: + if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent"); + break; + case AltosLib.ao_flight_landed: + if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed"); + break; + case AltosLib.ao_flight_stateless: + if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent"); + 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.callsign)) { Log.d(TAG, "update callsign"); mCallsignView.setText(state.callsign); @@ -276,7 +341,12 @@ public class AltosDroid extends FragmentActivity { } if (saved_state == null || state.state != saved_state.state) { Log.d(TAG, "update state"); - mStateView.setText(state.state_name()); + 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) { Log.d(TAG, "update rssi"); @@ -285,10 +355,10 @@ public class AltosDroid extends FragmentActivity { } for (AltosDroidTab mTab : mTabs) - mTab.update_ui(state, from_receiver, saved_location, mTab == mTabsAdapter.currentItem()); + mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem()); if (state != null) - mAltosVoice.tell(state); + mAltosVoice.tell(state, from_receiver); saved_state = state; } @@ -330,8 +400,6 @@ public class AltosDroid extends FragmentActivity { super.onCreate(savedInstanceState); if(D) Log.e(TAG, "+++ ON CREATE +++"); - fm = getSupportFragmentManager(); - // Get local Bluetooth adapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -339,12 +407,9 @@ public class AltosDroid extends FragmentActivity { if (mBluetoothAdapter == null) { Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); finish(); - return; } - // Initialise preferences - prefs = new AltosDroidPreferences(this); - AltosPreferences.init(prefs); + fm = getSupportFragmentManager(); // Set up the window layout requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); @@ -387,7 +452,6 @@ public class AltosDroid extends FragmentActivity { for (int i = 0; i < 5; i++) mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight; - // Set up the custom title mTitle = (TextView) findViewById(R.id.title_left_text); mTitle.setText(R.string.app_name); @@ -403,11 +467,10 @@ public class AltosDroid extends FragmentActivity { 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); - timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 100L); - mAltosVoice = new AltosVoice(this); } @@ -416,15 +479,16 @@ public class AltosDroid extends FragmentActivity { super.onStart(); if(D) Log.e(TAG, "++ ON START ++"); + // Start Telemetry Service + startService(new Intent(AltosDroid.this, TelemetryService.class)); + if (!mBluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - startActivityForResult(enableIntent, REQUEST_ENABLE_BT); + startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT); } - // Start Telemetry Service - startService(new Intent(AltosDroid.this, TelemetryService.class)); - doBindService(); + } @Override @@ -453,6 +517,7 @@ public class AltosDroid extends FragmentActivity { if(D) Log.e(TAG, "--- ON DESTROY ---"); if (mAltosVoice != null) mAltosVoice.stop(); + stop_timer(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -480,19 +545,21 @@ public class AltosDroid extends FragmentActivity { } } - private void connectDevice(Intent data) { - // Get the device MAC address - String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); - // Get the BLuetoothDevice object - BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + private void connectDevice(String address) { // Attempt to connect to the device try { - if (D) Log.d(TAG, "Connecting to " + device.getName()); - mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, device)); + if (D) Log.d(TAG, "Connecting to " + address); + mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address)); } catch (RemoteException e) { } } + private void connectDevice(Intent data) { + // Get the device MAC address + String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); + connectDevice(address); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -550,6 +617,11 @@ public class AltosDroid extends FragmentActivity { serverIntent = new Intent(this, DeviceListActivity.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); return true; + case R.id.quit: + Log.d(TAG, "R.id.quit"); + stopService(new Intent(AltosDroid.this, TelemetryService.class)); + finish(); + return true; case R.id.select_freq: // Set the TBT radio frequency @@ -597,6 +669,10 @@ public class AltosDroid extends FragmentActivity { 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; } return false; } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 70d7def8..9cef1319 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Mike Beattie <mike@ethernal.org> + * 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 @@ -14,88 +14,38 @@ * 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 org.altusmetrum.altoslib_5.*; -public class AltosDroidPreferences implements AltosPreferencesBackend { - public final static String NAME = "org.altusmetrum.AltosDroid"; - private Context context = null; - private SharedPreferences prefs = null; - private SharedPreferences.Editor editor = null; - - public AltosDroidPreferences(Context in_context) { - this(in_context, NAME); - } - - public AltosDroidPreferences(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(); - return (String[])all.keySet().toArray(); - } - - public AltosPreferencesBackend node(String key) { - return new AltosDroidPreferences(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) { - return prefs.getString(key, def); - } +public class AltosDroidPreferences extends AltosPreferences { - public void putBoolean(String key, boolean value) { - editor.putBoolean(key, value); - } + /* Active device preference name */ + final static String activeDevicePreference = "ACTIVE-DEVICE"; - public void putDouble(String key, double value) { - editor.putFloat(key, (float)value); - } + static String active_device_address; - public void putInt(String key, int value) { - editor.putInt(key, value); - } + public static void init(Context context) { + if (backend != null) + return; - public void putString(String key, String value) { - editor.putString(key, value); - } + AltosPreferences.init(new AltosDroidPreferencesBackend(context)); - public void remove(String key) { - editor.remove(key); + active_device_address = backend.getString(activeDevicePreference, null); } - public void flush() { - editor.apply(); + public static void set_active_device(String address) { + synchronized(backend) { + active_device_address = address; + backend.putString(activeDevicePreference, active_device_address); + flush_preferences(); + } } - public File homeDirectory() { - return Environment.getExternalStorageDirectory(); + public static String active_device() { + synchronized(backend) { + return active_device_address; + } } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java new file mode 100644 index 00000000..be41ae7c --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -0,0 +1,101 @@ +/* + * 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; 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.io.File; +import java.util.Map; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Environment; + +import org.altusmetrum.altoslib_5.*; + +public class AltosDroidPreferencesBackend implements 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(); + return (String[])all.keySet().toArray(); + } + + public AltosPreferencesBackend node(String key) { + 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) { + return prefs.getString(key, def); + } + + 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) { + editor.putString(key, value); + } + + public void remove(String key) { + editor.remove(key); + } + + public void flush() { + editor.apply(); + } + + public File homeDirectory() { + return Environment.getExternalStorageDirectory(); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index b960eb1a..8e625da6 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -27,8 +27,9 @@ import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentManager; import android.location.Location; import android.util.Log; +import android.widget.TextView; -public abstract class AltosDroidTab extends Fragment { +public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener { AltosState last_state; AltosGreatCircle last_from_receiver; Location last_receiver; @@ -37,39 +38,46 @@ public abstract class AltosDroidTab extends Fragment { public abstract String tab_name(); + public void units_changed(boolean imperial_units) { + if (!isHidden() && last_state != null) + show(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(); - if (visible) + if (visible) { + AltosState state = last_state; + AltosGreatCircle from_receiver = last_from_receiver; + Location receiver = last_receiver; + + show(state, from_receiver, receiver); ft.show(this); - else + } else ft.hide(this); ft.commit(); } public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) { + last_state = state; + last_from_receiver = from_receiver; + last_receiver = receiver; if (is_current) { - Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name())); + if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name())); show(state, from_receiver, receiver); } else { - Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name())); - last_state = state; - last_from_receiver = from_receiver; - last_receiver = receiver; + if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name())); return; } } - - public void onHiddenChanged(boolean hidden) { - if (last_state != null && isVisible()) { - AltosState state = last_state; - AltosGreatCircle from_receiver = last_from_receiver; - Location receiver = last_receiver; - - last_state = null; - last_from_receiver = null; - last_receiver = null; - show(state, from_receiver, receiver); - } - } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java index b05913b6..969992d3 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java @@ -58,7 +58,7 @@ public class AltosVoice { } } - public void tell(AltosState state) { + public void tell(AltosState state, AltosGreatCircle from_receiver) { if (!tts_enabled) return; boolean spoke = false; @@ -68,12 +68,14 @@ public class AltosVoice { if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) && state.state > AltosLib.ao_flight_boost) { if (state.max_speed() != AltosLib.MISSING) - speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5))); + speak(String.format("Max speed: %s.", + AltosConvert.speed.say_units(state.max_speed()))); spoke = true; } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) && state.state >= AltosLib.ao_flight_drogue) { if (state.max_height() != AltosLib.MISSING) - speak(String.format("max height: %d meters.", (int) (state.max_height() + 0.5))); + speak(String.format("Max height: %s.", + AltosConvert.height.say_units(state.max_height()))); spoke = true; } } @@ -88,13 +90,14 @@ public class AltosVoice { } old_state = state; if (idle_thread != null) - idle_thread.notice(state, spoke); + idle_thread.notice(state, from_receiver, spoke); } class IdleThread extends Thread { boolean started; private AltosState state; + private AltosGreatCircle from_receiver; int reported_landing; int report_interval; long report_time; @@ -112,29 +115,30 @@ public class AltosVoice { return; } - /* If the rocket isn't on the pad, then report height */ - if (((AltosLib.ao_flight_drogue <= state.state && + /* If the rocket isn't on the pad, then report location */ + if ((AltosLib.ao_flight_drogue <= state.state && state.state < AltosLib.ao_flight_landed) || - state.state == AltosLib.ao_flight_stateless) && - state.range >= 0) + state.state == AltosLib.ao_flight_stateless) { - if (state.from_pad != null) { - speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n", - (int) (state.height() + 0.5), - state.from_pad.bearing_words( + AltosGreatCircle position; + + if (from_receiver != null) + position = from_receiver; + else + position = state.from_pad; + + if (position != null) { + speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n", + AltosConvert.height.say_units(state.height()), + position.bearing_words( AltosGreatCircle.BEARING_VOICE), - (int) (state.from_pad.bearing + 0.5), - (int) (state.elevation + 0.5), - (int) (state.range + 0.5))); - } else { - speak(String.format("Height %d, elevation %d, range %d.\n", - (int) (state.height() + 0.5), - (int) (state.elevation + 0.5), - (int) (state.range + 0.5))); + (int) (position.bearing + 0.5), + (int) (position.elevation + 0.5), + AltosConvert.distance.say_units(position.range))); } } else if (state.state > AltosLib.ao_flight_pad) { if (state.height() != AltosLib.MISSING) - speak(String.format("%d meters", (int) (state.height() + 0.5))); + speak(AltosConvert.height.say_units(state.height())); } else { reported_landing = 0; } @@ -153,9 +157,9 @@ public class AltosVoice { else speak("rocket may have crashed"); if (state.from_pad != null) - speak(String.format("Bearing %d degrees, range %d meters.", + speak(String.format("Bearing %d degrees, range %s.", (int) (state.from_pad.bearing + 0.5), - (int) (state.from_pad.distance + 0.5))); + AltosConvert.distance.say_units(state.from_pad.distance))); ++reported_landing; } } @@ -186,9 +190,10 @@ public class AltosVoice { } } - public synchronized void notice(AltosState new_state, boolean spoken) { + public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) { AltosState old_state = state; state = new_state; + from_receiver = new_from_receiver; if (!started && state.state > AltosLib.ao_flight_pad) { started = true; start(); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java index 8e8d9c03..267c90f8 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java @@ -20,6 +20,7 @@ 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; @@ -51,14 +52,27 @@ public class GoNoGoLights { missing = m; set = true; if (missing) { + hide(); red.setImageDrawable(dGray); green.setImageDrawable(dGray); } else if (state) { red.setImageDrawable(dGray); green.setImageDrawable(dGreen); + show(); } else { red.setImageDrawable(dRed); green.setImageDrawable(dGray); + show(); } } + + public void show() { + red.setVisibility(View.VISIBLE); + green.setVisibility(View.VISIBLE); + } + + public void hide() { + red.setVisibility(View.GONE); + green.setVisibility(View.GONE); + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java index c146c277..fa4e3c8b 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java @@ -91,12 +91,13 @@ public class TabAscent extends AltosDroidTab { public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { - mHeightView.setText(AltosDroid.number("%6.0f m", state.height())); - mMaxHeightView.setText(AltosDroid.number("%6.0f m", state.max_height())); - mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed())); - mMaxSpeedView.setText(AltosDroid.number("%6.0f m/s", state.max_speed())); - mAccelView.setText(AltosDroid.number("%6.0f m/s²", state.acceleration())); - mMaxAccelView.setText(AltosDroid.number("%6.0f m/s²", state.max_acceleration())); + set_value(mHeightView, AltosConvert.height, 6, state.height()); + set_value(mHeightView, AltosConvert.height, 6, state.height()); + set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height()); + set_value(mSpeedView, AltosConvert.speed, 6, state.speed()); + set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed()); + set_value(mAccelView, AltosConvert.accel, 6, state.acceleration()); + set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration()); if (state.gps != null) { mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S")); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java index 6d781efd..28068666 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java @@ -93,14 +93,14 @@ public class TabDescent extends AltosDroidTab { public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { - mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed())); - mHeightView.setText(AltosDroid.number("%6.0f m", state.height())); + set_value(mSpeedView, AltosConvert.speed, 6, state.speed()); + set_value(mHeightView, AltosConvert.height, 6, state.height()); if (from_receiver != null) { mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation)); - mRangeView.setText(AltosDroid.number("%6.0f m", from_receiver.range)); + set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range); mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing)); mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG)); - mDistanceView.setText(AltosDroid.number("%6.0f m", from_receiver.distance)); + set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); } else { mElevationView.setText("<unknown>"); mRangeView.setText("<unknown>"); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java index 16427d8b..b257b936 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java @@ -78,7 +78,7 @@ public class TabLanded extends AltosDroidTab { public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); - mDistanceView.setText(String.format("%6.0f m", from_receiver.distance)); + set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); } if (state != null && state.gps != null) { mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S")); @@ -91,9 +91,9 @@ public class TabLanded extends AltosDroidTab { } if (state != null) { - mMaxHeightView.setText(String.format("%6.0f m", state.max_height())); - mMaxAccelView.setText(String.format("%6.0f m/s²", state.max_acceleration())); - mMaxSpeedView.setText(String.format("%6.0f m/s", state.max_speed())); + set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height()); + set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration()); + set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed()); } } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 811e5482..ab338ac2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -158,7 +158,7 @@ public class TabMap extends AltosDroidTab { public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); - mDistanceView.setText(String.format("%6.0f m", from_receiver.distance)); + set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); } if (state != null) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index 03b78b75..32df71d7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -33,10 +33,13 @@ public class TabPad extends AltosDroidTab { AltosDroid mAltosDroid; private TextView mBatteryVoltageView; + private TextView mBatteryVoltageLabel; private GoNoGoLights mBatteryLights; private TextView mApogeeVoltageView; + private TextView mApogeeVoltageLabel; private GoNoGoLights mApogeeLights; private TextView mMainVoltageView; + private TextView mMainVoltageLabel; private GoNoGoLights mMainLights; private TextView mDataLoggingView; private GoNoGoLights mDataLoggingLights; @@ -59,16 +62,19 @@ public class TabPad extends AltosDroidTab { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_pad, container, false); mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value); + mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label); mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled), (ImageView) v.findViewById(R.id.battery_greenled), getResources()); mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value); + mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label); mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), (ImageView) v.findViewById(R.id.apogee_greenled), getResources()); mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value); + mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label); mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), (ImageView) v.findViewById(R.id.main_greenled), getResources()); @@ -107,11 +113,23 @@ public class TabPad extends AltosDroidTab { if (state != null) { mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage)); mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING); - - mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); + if (state.apogee_voltage == AltosLib.MISSING) { + mApogeeVoltageView.setVisibility(View.GONE); + mApogeeVoltageLabel.setVisibility(View.GONE); + } else { + mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); + mApogeeVoltageView.setVisibility(View.VISIBLE); + mApogeeVoltageLabel.setVisibility(View.VISIBLE); + } mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING); - - mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage)); + if (state.main_voltage == AltosLib.MISSING) { + mMainVoltageView.setVisibility(View.GONE); + mMainVoltageLabel.setVisibility(View.GONE); + } else { + mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage)); + mMainVoltageView.setVisibility(View.VISIBLE); + mMainVoltageLabel.setVisibility(View.VISIBLE); + } mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING); if (state.flight != 0) { @@ -141,12 +159,12 @@ public class TabPad extends AltosDroidTab { } if (receiver != null) { - double altitude = 0; + double altitude = AltosLib.MISSING; if (receiver.hasAltitude()) altitude = receiver.getAltitude(); mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S")); mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E")); - mPadAltitudeView.setText(AltosDroid.number("%4.0f m", altitude)); + set_value(mPadAltitudeView, AltosConvert.height, 6, altitude); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 3ba5afa9..971c3e80 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -31,6 +31,7 @@ import org.altusmetrum.altoslib_5.*; public class TelemetryReader extends Thread { private static final String TAG = "TelemetryReader"; + private static final boolean D = true; int crc_errors; @@ -39,6 +40,8 @@ public class TelemetryReader extends Thread { AltosLink link; AltosState state = null; + AltosFlightReader stacked; + LinkedBlockingQueue<AltosLine> telemQueue; public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException { @@ -56,6 +59,10 @@ public class TelemetryReader extends Thread { public void close() { state = null; + if (stacked != null) { + stacked.close(false); + stacked = null; + } link.remove_monitor(telemQueue); link = null; telemQueue.clear(); @@ -66,7 +73,27 @@ public class TelemetryReader extends Thread { AltosState state = null; try { - for (;;) { + if (D) Log.d(TAG, "starting reader"); + while (stacked != null) { + AltosState stacked_state = null; + try { + stacked_state = stacked.read(); + } catch (ParseException pe) { + continue; + } catch (AltosCRCException ce) { + continue; + } + if (stacked_state != null) + state = stacked_state; + else + stacked = null; + } + if (state != null) { + if (D) Log.d(TAG, "Send initial state"); + handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget(); + } + if (D) Log.d(TAG, "starting loop"); + while (telemQueue != null) { try { state = read(); handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget(); @@ -84,12 +111,34 @@ public class TelemetryReader extends Thread { } } - public TelemetryReader (AltosLink in_link, Handler in_handler) { + public TelemetryReader (AltosLink in_link, Handler in_handler, AltosFlightReader in_stacked) { + if (D) Log.d(TAG, "connected TelemetryReader create started"); link = in_link; handler = in_handler; + stacked = in_stacked; state = null; telemQueue = new LinkedBlockingQueue<AltosLine>(); link.add_monitor(telemQueue); + link.set_telemetry(AltosLib.ao_telemetry_standard); + + if (D) Log.d(TAG, "connected TelemetryReader created"); + } + + private static AltosFlightReader existing_data(AltosLink link) { + if (link == null) + return null; + + File file = AltosPreferences.logfile(link.serial); + if (file != null) { + AltosStateIterable iterable = AltosStateIterable.iterable(file); + if (iterable != null) + return new AltosReplayReader(iterable.iterator(), file, false); + } + return null; + } + + public TelemetryReader(AltosLink link, Handler handler) { + this(link, handler, existing_data(link)); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 4ec353e3..30d94409 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -28,6 +28,7 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothAdapter; import android.content.Intent; import android.content.Context; import android.os.Bundle; @@ -63,11 +64,6 @@ public class TelemetryService extends Service implements LocationListener { static final int MSG_CRC_ERROR = 9; static final int MSG_SETBAUD = 10; - public static final int STATE_NONE = 0; - public static final int STATE_READY = 1; - public static final int STATE_CONNECTING = 2; - public static final int STATE_CONNECTED = 3; - // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. private int NOTIFICATION = R.string.telemetry_service_label; @@ -81,21 +77,17 @@ public class TelemetryService extends Service implements LocationListener { final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler. // Name of the connected device - private BluetoothDevice device = null; + String address; private AltosBluetooth mAltosBluetooth = null; - private AltosConfigData mConfigData = null; private TelemetryReader mTelemetryReader = null; private TelemetryLogger mTelemetryLogger = null; + // Local Bluetooth adapter + private BluetoothAdapter mBluetoothAdapter = null; - // internally track state of bluetooth connection - private int state = STATE_NONE; + private TelemetryState telemetry_state; // Last data seen; send to UI when it starts - private AltosState last_state; - private Location last_location; - private int last_crc_errors; - // Handler of incoming messages from clients. static class IncomingHandler extends Handler { private final WeakReference<TelemetryService> service; @@ -104,17 +96,15 @@ public class TelemetryService extends Service implements LocationListener { @Override public void handleMessage(Message msg) { TelemetryService s = service.get(); + if (s == null) + return; switch (msg.what) { case MSG_REGISTER_CLIENT: s.mClients.add(msg.replyTo); try { // Now we try to send the freshly connected UI any relavant information about what - // we're talking to - Basically state and Config Data. - msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, s.state, -1, s.mConfigData)); - // We also send any recent telemetry or location data that's cached - if (s.last_state != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_TELEMETRY, s.last_state )); - if (s.last_location != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_LOCATION , s.last_location )); - if (s.last_crc_errors != 0 ) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, s.last_crc_errors)); + // we're talking to + msg.replyTo.send(s.message()); } catch (RemoteException e) { s.mClients.remove(msg.replyTo); } @@ -126,47 +116,59 @@ public class TelemetryService extends Service implements LocationListener { break; case MSG_CONNECT: if (D) Log.d(TAG, "Connect command received"); - s.device = (BluetoothDevice) msg.obj; - s.startAltosBluetooth(); + String address = (String) msg.obj; + AltosDroidPreferences.set_active_device(address); + s.startAltosBluetooth(address); break; case MSG_CONNECTED: if (D) Log.d(TAG, "Connected to device"); - s.connected(); + try { + s.connected(); + } catch (InterruptedException ie) { + } break; case MSG_CONNECT_FAILED: if (D) Log.d(TAG, "Connection failed... retrying"); - s.startAltosBluetooth(); + if (s.address != null) + s.startAltosBluetooth(s.address); break; case MSG_DISCONNECTED: - // Only do the following if we haven't been shutdown elsewhere.. - if (s.device != null) { - if (D) Log.d(TAG, "Disconnected from " + s.device.getName()); - s.stopAltosBluetooth(); - } + Log.d(TAG, "MSG_DISCONNECTED"); + s.stopAltosBluetooth(); break; case MSG_TELEMETRY: // forward telemetry messages - s.last_state = (AltosState) msg.obj; - s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj)); + s.telemetry_state.state = (AltosState) msg.obj; + if (D) Log.d(TAG, "MSG_TELEMETRY"); + s.sendMessageToClients(); break; case MSG_CRC_ERROR: // forward crc error messages - s.last_crc_errors = (Integer) msg.obj; - s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, msg.obj)); + s.telemetry_state.crc_errors = (Integer) msg.obj; + if (D) Log.d(TAG, "MSG_CRC_ERROR"); + s.sendMessageToClients(); break; case MSG_SETFREQUENCY: - if (s.state == STATE_CONNECTED) { + if (D) Log.d(TAG, "MSG_SETFREQUENCY"); + s.telemetry_state.frequency = (Double) msg.obj; + if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) { try { - s.mAltosBluetooth.set_radio_frequency((Double) msg.obj); + s.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency); + s.mAltosBluetooth.save_frequency(); } catch (InterruptedException e) { } catch (TimeoutException e) { } } + s.sendMessageToClients(); break; case MSG_SETBAUD: - if (s.state == STATE_CONNECTED) { - s.mAltosBluetooth.set_telemetry_rate((Integer) msg.obj); + if (D) Log.d(TAG, "MSG_SETBAUD"); + s.telemetry_state.telemetry_rate = (Integer) msg.obj; + if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) { + s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate); + s.mAltosBluetooth.save_telemetry_rate(); } + s.sendMessageToClients(); break; default: super.handleMessage(msg); @@ -174,9 +176,18 @@ public class TelemetryService extends Service implements LocationListener { } } - private void sendMessageToClients(Message m) { + private Message message() { + if (telemetry_state == null) + Log.d(TAG, "telemetry_state null!"); + return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state); + } + + private void sendMessageToClients() { + Message m = message(); + if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size())); for (int i=mClients.size()-1; i>=0; i--) { try { + if (D) Log.d(TAG, String.format("Send message to client %d", i)); mClients.get(i).send(m); } catch (RemoteException e) { mClients.remove(i); @@ -186,7 +197,7 @@ public class TelemetryService extends Service implements LocationListener { private void stopAltosBluetooth() { if (D) Log.d(TAG, "stopAltosBluetooth(): begin"); - setState(STATE_READY); + telemetry_state.connect = TelemetryState.CONNECT_READY; if (mTelemetryReader != null) { if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader"); mTelemetryReader.interrupt(); @@ -206,18 +217,21 @@ public class TelemetryService extends Service implements LocationListener { mAltosBluetooth.close(); mAltosBluetooth = null; } - device = null; - mConfigData = null; + telemetry_state.config = null; + if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients"); + sendMessageToClients(); } - private void startAltosBluetooth() { - if (device == null) { - return; - } + private void startAltosBluetooth(String address) { + // Get the BLuetoothDevice object + BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + + this.address = address; if (mAltosBluetooth == null) { if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress())); mAltosBluetooth = new AltosBluetooth(device, mHandler); - setState(STATE_CONNECTING); + telemetry_state.connect = TelemetryState.CONNECT_CONNECTING; + sendMessageToClients(); } else { // This is a bit of a hack - if it appears we're still connected, we treat this as a restart. // So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message @@ -225,28 +239,19 @@ public class TelemetryService extends Service implements LocationListener { // ... then we tear down the existing connection. // We do it this way around so that we don't lose a reference to the device when this method // is called on reception of MSG_CONNECT_FAILED in the handler above. - mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, device), 3000); + mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, address), 3000); stopAltosBluetooth(); } } - private synchronized void setState(int s) { - if (D) Log.d(TAG, "setState(): " + state + " -> " + s); - state = s; - - // This shouldn't be required - mConfigData should be null for any non-connected - // state, but to be safe and to reduce message size - AltosConfigData acd = (state == STATE_CONNECTED) ? mConfigData : null; - - sendMessageToClients(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, state, -1, acd)); - } - - private void connected() { + private void connected() throws InterruptedException { + if (D) Log.d(TAG, "connected top"); try { if (mAltosBluetooth == null) throw new InterruptedException("no bluetooth"); - mConfigData = mAltosBluetooth.config_data(); - } catch (InterruptedException e) { + telemetry_state.config = mAltosBluetooth.config_data(); + mAltosBluetooth.set_radio_frequency(telemetry_state.frequency); + mAltosBluetooth.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. @@ -254,19 +259,25 @@ public class TelemetryService extends Service implements LocationListener { return; } - setState(STATE_CONNECTED); + if (D) Log.d(TAG, "connected bluetooth configured"); + telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler); mTelemetryReader.start(); - + + if (D) Log.d(TAG, "connected TelemetryReader started"); + mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth); - } + if (D) Log.d(TAG, "Notify UI of connection"); + + sendMessageToClients(); + } private void onTimerTick() { if (D) Log.d(TAG, "Timer wakeup"); try { - if (mClients.size() <= 0 && state != STATE_CONNECTED) { + if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) { stopSelf(); } } catch (Throwable t) { @@ -277,19 +288,35 @@ public class TelemetryService extends Service implements LocationListener { @Override public void onCreate() { + // 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(); + } + + // Initialise preferences + AltosDroidPreferences.init(this); + + telemetry_state = new TelemetryState(); + // Create a reference to the NotificationManager so that we can update our notifcation text later //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - setState(STATE_READY); + telemetry_state.connect = TelemetryState.CONNECT_READY; // Start our timer - first event in 10 seconds, then every 10 seconds after that. timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L); // Listen for GPS and Network position updates LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); - + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this); -// locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); + + String address = AltosDroidPreferences.active_device(); + if (address != null) + startAltosBluetooth(address); } @Override @@ -345,8 +372,9 @@ public class TelemetryService extends Service implements LocationListener { public void onLocationChanged(Location location) { - last_location = location; - sendMessageToClients(Message.obtain(null, AltosDroid.MSG_LOCATION, location)); + telemetry_state.location = location; + if (D) Log.d(TAG, "location changed"); + sendMessageToClients(); } public void onStatusChanged(String provider, int status, Bundle extras) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java new file mode 100644 index 00000000..d3341739 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -0,0 +1,46 @@ +/* + * 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; 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 org.altusmetrum.altoslib_5.*; +import android.location.Location; + +public class TelemetryState { + public static final int CONNECT_NONE = 0; + public static final int CONNECT_READY = 1; + public static final int CONNECT_CONNECTING = 2; + public static final int CONNECT_CONNECTED = 3; + + int connect; + AltosConfigData config; + AltosState state; + Location location; + int crc_errors; + double frequency; + int telemetry_rate; + + public TelemetryState() { + connect = CONNECT_NONE; + config = null; + state = null; + location = null; + crc_errors = 0; + frequency = AltosPreferences.frequency(0); + telemetry_rate = AltosPreferences.telemetry_rate(0); + } +} |