altosdroid: Add multi-tracker support
authorKeith Packard <keithp@keithp.com>
Sun, 7 Jun 2015 03:36:18 +0000 (20:36 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 7 Jun 2015 03:36:18 +0000 (20:36 -0700)
This lets you view multiple trackers in the offline maps tab (online
maps not done yet), saves state of each tracker to preferences.

Signed-off-by: Keith Packard <keithp@keithp.com>
14 files changed:
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java
altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java
altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java
altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java
altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java
altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java
altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java

index 8c9ff31f0b44d6e1777743d00bd974d8e4f75309..942ebbd5f06d1b4b50441b0677fb948ad23ec54c 100644 (file)
@@ -42,16 +42,8 @@ import android.content.res.Resources;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.util.DisplayMetrics;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.View;
-import android.view.LayoutInflater;
-import android.widget.TabHost;
-import android.widget.TextView;
-import android.widget.RelativeLayout;
-import android.widget.Toast;
+import android.view.*;
+import android.widget.*;
 import android.app.AlertDialog;
 import android.location.Location;
 import android.hardware.usb.*;
@@ -112,6 +104,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        // Timer and Saved flight state for Age calculation
        private Timer timer;
        AltosState saved_state;
+       TelemetryState  telemetry_state;
+       Integer[]       serials;
 
        UsbDevice       pending_usb_device;
        boolean         start_with_usb;
@@ -136,13 +130,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        switch (msg.what) {
                        case MSG_STATE:
                                AltosDebug.debug("MSG_STATE");
-                               TelemetryState telemetry_state = (TelemetryState) msg.obj;
-                               if (telemetry_state == null) {
+                               if (msg.obj == null) {
                                        AltosDebug.debug("telemetry_state null!");
                                        return;
                                }
-
-                               ad.update_state(telemetry_state);
+                               ad.update_state((TelemetryState) msg.obj);
                                break;
                        case MSG_UPDATE_AGE:
                                AltosDebug.debug("MSG_UPDATE_AGE");
@@ -258,17 +250,34 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
 
        boolean registered_units_listener;
 
-       void update_state(TelemetryState telemetry_state) {
+       int     current_serial;
+
+       void update_state(TelemetryState new_telemetry_state) {
+
+               if (new_telemetry_state != null)
+                       telemetry_state = new_telemetry_state;
+
+               if (current_serial == 0)
+                       current_serial = telemetry_state.latest_serial;
 
                if (!registered_units_listener) {
                        registered_units_listener = true;
                        AltosPreferences.register_units_listener(this);
                }
 
+               serials = telemetry_state.states.keySet().toArray(new Integer[0]);
+
                update_title(telemetry_state);
-               update_ui(telemetry_state.state, telemetry_state.location);
-               if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
-                       start_timer();
+
+               AltosDebug.debug("update state current serial %d\n", current_serial);
+
+               AltosState      state = null;
+               if (telemetry_state.states.containsKey(current_serial))
+                       state = telemetry_state.states.get(current_serial);
+
+               update_ui(telemetry_state, state, telemetry_state.location);
+
+               start_timer();
        }
 
        boolean same_string(String a, String b) {
@@ -283,22 +292,55 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
        }
 
+
+       private int blend_component(int a, int b, double r, int shift, int mask) {
+               return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
+       }
+       private int blend_color(int a, int b, double r) {
+               return (blend_component(a, b, r, 0, 0xff) |
+                       blend_component(a, b, r, 8, 0xff) |
+                       blend_component(a, b, r, 16, 0xff) |
+                       blend_component(a, b, r, 24, 0xff));
+       }
+
+       int state_age(AltosState state) {
+               return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000);
+       }
+
+       void set_screen_on(int age) {
+               if (age < 60)
+                       getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+               else
+                       getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+       }
+
        void update_age() {
                if (saved_state != null) {
-                       int age = (int) ((System.currentTimeMillis() - saved_state.received_time + 500) / 1000);
-                       boolean old = age >= 10;
-                       if (old != mAgeViewOld) {
-                               if (old)
-                                       mAgeView.setTextColor(mAgeOldColor);
-                               else
-                                       mAgeView.setTextColor(mAgeNewColor);
-                               mAgeViewOld = old;
-                       }
-                       mAgeView.setText(String.format("%d", age));
+                       int age = state_age(saved_state);
+
+                       double age_scale = age / 100.0;
+
+                       if (age_scale > 1.0)
+                               age_scale = 1.0;
+
+                       mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
+
+                       set_screen_on(age);
+
+                       String  text;
+                       if (age < 60)
+                               text = String.format("%ds", age);
+                       else if (age < 60 * 60)
+                               text = String.format("%dm", age / 60);
+                       else if (age < 60 * 60 * 24)
+                               text = String.format("%dh", age / (60 * 60));
+                       else
+                               text = String.format("%dd", age / (24 * 60 * 60));
+                       mAgeView.setText(text);
                }
        }
 
-       void update_ui(AltosState state, Location location) {
+       void update_ui(TelemetryState telem_state, AltosState state, Location location) {
 
                int prev_state = AltosLib.ao_flight_invalid;
 
@@ -308,6 +350,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        prev_state = saved_state.state;
 
                if (state != null) {
+                       set_screen_on(state_age(state));
+
                        if (state.state == AltosLib.ao_flight_stateless) {
                                boolean prev_locked = false;
                                boolean locked = false;
@@ -382,7 +426,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
 
                for (AltosDroidTab mTab : mTabs)
-                       mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
+                       mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
 
                if (state != null && mAltosVoice != null)
                        mAltosVoice.tell(state, from_receiver);
@@ -744,6 +788,26 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
        }
 
+       void select_tracker(int serial) {
+               int i;
+               for (i = 0; i < serials.length; i++)
+                       if (serials[i] == serial)
+                               break;
+               if (i == serials.length)
+                       return;
+
+               AltosDebug.debug("Switching to serial %d\n", serial);
+               current_serial = serial;
+               update_state(null);
+       }
+
+       void delete_track(int serial) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
+               } catch (Exception ex) {
+               }
+       }
+
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                Intent serverIntent = null;
@@ -824,8 +888,43 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        serverIntent = new Intent(this, MapTypeActivity.class);
                        startActivityForResult(serverIntent, REQUEST_MAP_TYPE);
                        return true;
+               case R.id.select_tracker:
+                       if (serials != null) {
+                               String[] trackers = new String[serials.length];
+                               for (int i = 0; i < serials.length; i++)
+                                       trackers[i] = String.format("%d", serials[i]);
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Select a tracker");
+                               builder_serial.setItems(trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       select_tracker(serials[item]);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
+               case R.id.delete_track:
+                       if (serials != null) {
+                               String[] trackers = new String[serials.length];
+                               for (int i = 0; i < serials.length; i++)
+                                       trackers[i] = String.format("%d", serials[i]);
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Delete a track");
+                               builder_serial.setItems(trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       delete_track(serials[item]);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
                }
                return false;
        }
-
 }
index 75676e284909d4e72038ccb0e827d75cb0f4d1fc..dfc3715323dbaad67db4b2fe93853823a0539119 100644 (file)
@@ -44,7 +44,12 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
 
        public String[] keys() {
                Map<String, ?> all = prefs.getAll();
-               return (String[])all.keySet().toArray();
+               Object[] ao = all.keySet().toArray();
+
+               String[] as = new String[ao.length];
+               for (int i = 0; i < ao.length; i++)
+                       as[i] = (String) ao[i];
+               return as;
        }
 
        public AltosPreferencesBackend node(String key) {
@@ -104,6 +109,7 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
        }
 
        public void remove(String key) {
+               AltosDebug.debug("remove preference %s\n", key);
                editor.remove(key);
        }
 
index f1f1b6de853e6996034d23d305b8b51a909fb2a4..017315af6c24ef6ae4e739a5286f9fe17f850dbd 100644 (file)
@@ -29,11 +29,13 @@ import android.location.Location;
 import android.widget.TextView;
 
 public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
+       TelemetryState          last_telem_state;
        AltosState              last_state;
        AltosGreatCircle        last_from_receiver;
        Location                last_receiver;
+       AltosDroid              altos_droid;
 
-       public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver);
+       public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
 
        public abstract String tab_name();
 
@@ -41,8 +43,8 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen
        }
 
        public void units_changed(boolean imperial_units) {
-               if (!isHidden() && last_state != null)
-                       show(last_state, last_from_receiver, last_receiver);
+               if (!isHidden())
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
        }
 
        public void set_value(TextView text_view,
@@ -59,17 +61,27 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen
                FragmentTransaction     ft = AltosDroid.fm.beginTransaction();
                AltosDebug.debug("set visible %b %s\n", visible, tab_name());
                if (visible) {
-                       AltosState              state = last_state;
-                       AltosGreatCircle        from_receiver = last_from_receiver;
-                       Location                receiver = last_receiver;
-
                        ft.show(this);
-                       show(state, from_receiver, receiver);
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
                } else
                        ft.hide(this);
                ft.commitAllowingStateLoss();
        }
 
+       @Override
+       public void onAttach(Activity activity) {
+               super.onAttach(activity);
+               altos_droid = (AltosDroid) activity;
+               altos_droid.registerTab(this);
+       }
+
+       @Override
+       public void onDetach() {
+               super.onDetach();
+               altos_droid.unregisterTab(this);
+               altos_droid = null;
+       }
+
        @Override
        public void onResume() {
                super.onResume();
@@ -77,12 +89,16 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen
                set_visible(true);
        }
 
-       public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+       public void update_ui(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver, boolean is_current)
+       {
+               last_telem_state = telem_state;
                last_state = state;
                last_from_receiver = from_receiver;
                last_receiver = receiver;
+               AltosDebug.debug("update_ui tab %s is_current %b\n", tab_name(), is_current);
                if (is_current)
-                       show(state, from_receiver, receiver);
+                       show(telem_state, state, from_receiver, receiver);
                else
                        return;
        }
index 4af117a29bbde343c91a0a0b8c6416aa0836280d..f36ef267c9c5c2660c5431e72638a388aa85125f 100644 (file)
@@ -191,14 +191,22 @@ public class DeviceListActivity extends Activity {
 
                        // When discovery finds a device
                        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
-                               // Get the BluetoothDevice object from the Intent
+
+                               /* Get the BluetoothDevice object from the Intent
+                                */
                                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                               // If it's already paired, skip it, because it's been listed already
-                               if (   device.getBondState() != BluetoothDevice.BOND_BONDED
-                                   && device.getName().startsWith("TeleBT")               ) {
-                                       mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
+                               /* If it's already paired, skip it, because it's been listed already
+                                */
+                               if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
+                               {
+                                       String  name = device.getName();
+                                       if (name != null && name.startsWith("TeleBT"))
+                                               mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                                }
-                       // When discovery is finished, change the Activity title
+
+                       /* When discovery is finished, change the Activity title
+                        */
                        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                                setProgressBarIndeterminateVisibility(false);
                                setTitle(R.string.select_device);
index 797dc7c3ceee8b991420a7b65730b25d1d826e4e..afce937f415f3d8d88122a219c935a0c9adcc3f7 100644 (file)
@@ -30,8 +30,6 @@ import android.widget.TextView;
 import android.location.Location;
 
 public class TabAscent extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
        private TextView mHeightView;
        private TextView mMaxHeightView;
        private TextView mSpeedView;
@@ -45,13 +43,6 @@ public class TabAscent extends AltosDroidTab {
        private TextView mMainVoltageView;
        private GoNoGoLights mMainLights;
 
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_ascent, container, false);
@@ -78,18 +69,11 @@ public class TabAscent extends AltosDroidTab {
                return v;
        }
 
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
        public String tab_name() {
                return "ascent";
        }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (state != null) {
                        set_value(mHeightView, AltosConvert.height, 6, state.height());
                        set_value(mHeightView, AltosConvert.height, 6, state.height());
index f3f5a0f15d841258c7439fe38a9c2eb73be3a167..3429ee721ee734b36a120f508aef1480ef6c722f 100644 (file)
@@ -30,8 +30,6 @@ import android.widget.TextView;
 import android.location.Location;
 
 public class TabDescent extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
        private TextView mSpeedView;
        private TextView mHeightView;
        private TextView mElevationView;
@@ -46,14 +44,6 @@ public class TabDescent extends AltosDroidTab {
        private TextView mMainVoltageView;
        private GoNoGoLights mMainLights;
 
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_descent, container, false);
@@ -81,17 +71,9 @@ public class TabDescent extends AltosDroidTab {
                return v;
        }
 
-
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
        public String tab_name() { return "descent"; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (state != null) {
                        set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
                        set_value(mHeightView, AltosConvert.height, 6, state.height());
index d37891f793aff866e2c0130652044ac8c92929aa..dd3f938edd501c56454913c9e0001f37c5f0a32f 100644 (file)
@@ -29,8 +29,6 @@ import android.widget.TextView;
 import android.location.Location;
 
 public class TabLanded extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
        private TextView mBearingView;
        private TextView mDistanceView;
        private TextView mTargetLatitudeView;
@@ -41,14 +39,6 @@ public class TabLanded extends AltosDroidTab {
        private TextView mMaxSpeedView;
        private TextView mMaxAccelView;
 
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_landed, container, false);
@@ -66,16 +56,9 @@ public class TabLanded extends AltosDroidTab {
                return v;
        }
 
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
        public String tab_name() { return "landed"; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
                        mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
                        set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
index cea3cac60b28dd5021b155d31b5bb9e3bba34cb6..ff36875db9470b1f77dbd4bd21e458a7d2137bf3 100644 (file)
@@ -33,6 +33,7 @@ import com.google.android.gms.maps.model.PolylineOptions;
 
 import android.app.Activity;
 import android.graphics.Color;
+import android.graphics.*;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 //import android.support.v4.app.FragmentTransaction;
@@ -43,8 +44,6 @@ import android.widget.TextView;
 import android.location.Location;
 
 public class TabMap extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
        private SupportMapFragment mMapFragment;
        private GoogleMap mMap;
        private boolean mapLoaded = false;
@@ -63,11 +62,40 @@ public class TabMap extends AltosDroidTab {
 
        private double mapAccuracy = -1;
 
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
+       private Bitmap rocket_bitmap(String text) {
+
+               /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
+                */
+               Bitmap orig_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
+               Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
+
+               Canvas canvas = new Canvas(bitmap);
+               Paint paint = new Paint();
+               paint.setTextSize(40);
+               paint.setColor(0xff000000);
+
+               Rect    bounds = new Rect();
+               paint.getTextBounds(text, 0, text.length(), bounds);
+
+               int     width = bounds.right - bounds.left;
+               int     height = bounds.bottom - bounds.top;
+
+               float x = bitmap.getWidth() / 2.0f - width / 2.0f;
+               float y = bitmap.getHeight() / 2.0f - height / 2.0f;
+
+               AltosDebug.debug("map label x %f y %f\n", x, y);
+
+               canvas.drawText(text, 0, text.length(), x, y, paint);
+               return bitmap;
+       }
+
+       private Marker rocket_marker(int serial, double lat, double lon) {
+               Bitmap  bitmap = rocket_bitmap(String.format("%d", serial));
+
+               return mMap.addMarker(new MarkerOptions()
+                                     .icon(BitmapDescriptorFactory.fromBitmap(bitmap))
+                                     .position(new LatLng(lat, lon))
+                                     .visible(false));
        }
 
        @Override
@@ -102,33 +130,17 @@ public class TabMap extends AltosDroidTab {
                getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
        }
 
-       @Override
-       public void onDestroyView() {
-               super.onDestroyView();
-
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-
-               //Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
-               //FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
-               //ft.remove(fragment);
-               //ft.commit();
-       }
-
        private void setupMap() {
                mMap = mMapFragment.getMap();
                if (mMap != null) {
-                       set_map_type(mAltosDroid.map_type);
+                       set_map_type(altos_droid.map_type);
                        mMap.setMyLocationEnabled(true);
                        mMap.getUiSettings().setTiltGesturesEnabled(false);
                        mMap.getUiSettings().setZoomControlsEnabled(false);
 
-                       mRocketMarker = mMap.addMarker(
-                                       // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
-                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.rocket))
-                                                          .position(new LatLng(0,0))
-                                                          .visible(false)
-                                       );
+                       Bitmap label_bitmap = rocket_bitmap("hello");
+
+                       mRocketMarker = rocket_marker(1800,0,0);
 
                        mPadMarker = mMap.addMarker(
                                        new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
@@ -156,7 +168,7 @@ public class TabMap extends AltosDroidTab {
 
        public String tab_name() { return "map"; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
                        mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
                        set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
@@ -166,6 +178,8 @@ public class TabMap extends AltosDroidTab {
                        if (mapLoaded) {
                                if (state.gps != null) {
                                        mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
+                                       mRocketMarker.setTitle("hello world");
+                                       mRocketMarker.setSnippet("hello");
                                        mRocketMarker.setVisible(true);
 
                                        mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
index 56e296d9eddd9652999ebd851dd436fdcfe8da56..4b728c23ffca828488197511db27d9a744a7cb00 100644 (file)
@@ -54,8 +54,6 @@ class Rocket {
 
 public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
 
-       AltosDroid mAltosDroid;
-
        AltosMap map;
 
        AltosLatLon     here;
@@ -325,11 +323,9 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
        @Override
        public void onAttach(Activity activity) {
                super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
 
                map = new AltosMap(this);
-               map.set_maptype(mAltosDroid.map_type);
+               map.set_maptype(altos_droid.map_type);
 
                pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
                /* arrow at the bottom of the launchpad image */
@@ -347,12 +343,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
                here_off_y = here_bitmap.getHeight() / 2;
        }
 
-       @Override
-       public void onDetach() {
-               super.onDetach();
-               mAltosDroid = null;
-       }
-
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
@@ -382,7 +372,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
        public void onDestroyView() {
                super.onDestroyView();
 
-               mAltosDroid.unregisterTab(this);
        }
 
        private void center(double lat, double lon, double accuracy) {
@@ -395,7 +384,7 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
 
        public String tab_name() { return "offmap"; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
                        mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
                        set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
@@ -411,20 +400,30 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
                        }
                        if (state.pad_lat != AltosLib.MISSING && pad == null)
                                pad = new AltosLatLon(state.pad_lat, state.pad_lon);
+               }
 
-                       int serial = state.serial;
-                       if (serial == AltosLib.MISSING)
-                               serial = 0;
+               if (telem_state != null) {
+                       Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
+                       Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
 
-                       Rocket  rocket = null;
+                       /* remove deleted keys */
+                       for (int serial : old_serial) {
+                               if (!telem_state.states.containsKey(serial))
+                                       rockets.remove(serial);
+                       }
+
+                       /* set remaining keys */
 
-                       if (state.gps != null && state.gps.locked) {
-                               if (!rockets.containsKey(serial)) {
+                       for (int serial : new_serial) {
+                               Rocket          rocket;
+                               AltosState      t_state = telem_state.states.get(serial);
+                               if (rockets.containsKey(serial))
+                                       rocket = rockets.get(serial);
+                               else {
                                        rocket = new Rocket(String.format("%d", serial), this);
                                        rockets.put(serial, rocket);
-                               } else
-                                       rocket = rockets.get(serial);
-                               rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon));
+                               }
+                               rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon));
                        }
                }
 
index f7eb43db8e53c9d72af2d18017459b2e9dfa5829..7a256963c27c460929cee19d2126d7aff6c47cd2 100644 (file)
@@ -30,8 +30,6 @@ import android.widget.TextView;
 import android.location.Location;
 
 public class TabPad extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
        private TextView mBatteryVoltageView;
        private TextView mBatteryVoltageLabel;
        private GoNoGoLights mBatteryLights;
@@ -51,13 +49,6 @@ public class TabPad extends AltosDroidTab {
        private TextView mPadLongitudeView;
        private TextView mPadAltitudeView;
 
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_pad, container, false);
@@ -100,16 +91,9 @@ public class TabPad extends AltosDroidTab {
         return v;
        }
 
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
        public String tab_name() { return "pad"; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                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);
index 7c3c2268c0ae8bf70d59ce7d654d7bfa09130eae..28bab40122cb796a7932fc2e706aee869d78bbe1 100644 (file)
@@ -34,7 +34,7 @@ public class TelemetryLogger {
                        logger = null;
                }
        }
-       
+
        void handleExternalStorageState() {
                String state = Environment.getExternalStorageState();
                if (Environment.MEDIA_MOUNTED.equals(state)) {
index 7b29fe44a2f91a9742652f89dcc6c9b96f3d431a..4d4f0ed1971d9f9f0ebc400729715cb48a9dfbb8 100644 (file)
@@ -21,6 +21,7 @@ package org.altusmetrum.AltosDroid;
 
 import java.text.*;
 import java.io.*;
+import java.util.*;
 import java.util.concurrent.*;
 import android.os.Handler;
 
@@ -34,25 +35,18 @@ public class TelemetryReader extends Thread {
        Handler     handler;
 
        AltosLink   link;
-       AltosState  state = null;
 
        LinkedBlockingQueue<AltosLine> telemQueue;
 
-       public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
+       public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
                AltosLine l = telemQueue.take();
                if (l.line == null)
                        throw new IOException("IO error");
                AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
-               if (state == null)
-                       state = new AltosState();
-               else
-                       state = state.clone();
-               telem.update_state(state);
-               return state;
+               return telem;
        }
 
        public void close() {
-               state = null;
                link.remove_monitor(telemQueue);
                link = null;
                telemQueue.clear();
@@ -60,14 +54,12 @@ public class TelemetryReader extends Thread {
        }
 
        public void run() {
-               AltosState  state = null;
-
                try {
                        AltosDebug.debug("starting loop");
                        while (telemQueue != null) {
                                try {
-                                       state = read();
-                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+                                       AltosTelemetry  telem = read();
+                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
                                } catch (ParseException pp) {
                                        AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
                                } catch (AltosCRCException ce) {
@@ -84,12 +76,11 @@ public class TelemetryReader extends Thread {
                }
        }
 
-       public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) {
+       public TelemetryReader (AltosLink in_link, Handler in_handler) {
                AltosDebug.debug("connected TelemetryReader create started");
                link    = in_link;
                handler = in_handler;
 
-               state = in_state;
                telemQueue = new LinkedBlockingQueue<AltosLine>();
                link.add_monitor(telemQueue);
                link.set_telemetry(AltosLib.ao_telemetry_standard);
index eae360db3913e7678aa1e1cd19e3658737bf49d5..80694ea74357385264f19c5f5cd0968a25d69b8c 100644 (file)
 package org.altusmetrum.AltosDroid;
 
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 import java.util.concurrent.TimeoutException;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.*;
 
 import android.app.Notification;
 //import android.app.NotificationManager;
@@ -62,6 +60,7 @@ public class TelemetryService extends Service implements LocationListener {
        static final int MSG_CRC_ERROR         = 10;
        static final int MSG_SETBAUD           = 11;
        static final int MSG_DISCONNECT        = 12;
+       static final int MSG_DELETE_SERIAL     = 13;
 
        // Unique Identification Number for the Notification.
        // We use it on Notification start, and to cancel it.
@@ -120,6 +119,10 @@ public class TelemetryService extends Service implements LocationListener {
                                s.address = null;
                                s.disconnect(true);
                                break;
+                       case MSG_DELETE_SERIAL:
+                               AltosDebug.debug("Delete Serial command received");
+                               s.delete_serial((Integer) msg.obj);
+                               break;
                        case MSG_SETFREQUENCY:
                                AltosDebug.debug("MSG_SETFREQUENCY");
                                s.telemetry_state.frequency = (Double) msg.obj;
@@ -197,13 +200,8 @@ public class TelemetryService extends Service implements LocationListener {
                                 * Messages from TelemetryReader
                                 */
                        case MSG_TELEMETRY:
-                               s.telemetry_state.state = (AltosState) msg.obj;
-                               if (s.telemetry_state.state != null) {
-                                       AltosDebug.debug("Save state");
-                                       AltosPreferences.set_state(0, s.telemetry_state.state, null);
-                               }
                                AltosDebug.debug("MSG_TELEMETRY");
-                               s.send_to_clients();
+                               s.telemetry((AltosTelemetry) msg.obj);
                                break;
                        case MSG_CRC_ERROR:
                                // forward crc error messages
@@ -217,13 +215,31 @@ public class TelemetryService extends Service implements LocationListener {
                }
        }
 
+       /* Handle telemetry packet
+        */
+       private void telemetry(AltosTelemetry telem) {
+               AltosState      state;
+
+               if (telemetry_state.states.containsKey(telem.serial))
+                       state = telemetry_state.states.get(telem.serial).clone();
+               else
+                       state = new AltosState();
+               telem.update_state(state);
+               telemetry_state.states.put(telem.serial, state);
+               if (state != null) {
+                       AltosDebug.debug("Save state %d", telem.serial);
+                       AltosPreferences.set_state(telem.serial, state, null);
+               }
+               send_to_clients();
+       }
+
        /* Construct the message to deliver to clients
         */
        private Message message() {
                if (telemetry_state == null)
                        AltosDebug.debug("telemetry_state null!");
-               if (telemetry_state.state == null)
-                       AltosDebug.debug("telemetry_state.state null!");
+               if (telemetry_state.states == null)
+                       AltosDebug.debug("telemetry_state.states null!");
                return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
        }
 
@@ -332,6 +348,12 @@ public class TelemetryService extends Service implements LocationListener {
                }
        }
 
+       private void delete_serial(int serial) {
+               telemetry_state.states.remove((Integer) serial);
+               AltosPreferences.remove_state(serial);
+               send_to_clients();
+       }
+
        private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
                // Get the BLuetoothDevice object
                BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
@@ -372,7 +394,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_state.state);
+               telemetry_reader = new TelemetryReader(altos_link, handler);
                telemetry_reader.start();
 
                AltosDebug.debug("connected TelemetryReader started");
@@ -406,11 +428,32 @@ public class TelemetryService extends Service implements LocationListener {
                telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
                telemetry_state.address = null;
 
-               AltosSavedState saved_state = AltosPreferences.state(0);
+               /* Pull the saved state information out of the preferences database
+                */
+               ArrayList<Integer> serials = AltosPreferences.list_states();
 
-               if (saved_state != null) {
-                       AltosDebug.debug("recovered old state flight %d\n", saved_state.state.flight);
-                       telemetry_state.state = saved_state.state;
+               telemetry_state.latest_serial = AltosPreferences.latest_state();
+
+               for (int serial : serials) {
+                       AltosSavedState 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",
+                                                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);
+                       }
                }
 
                // Listen for GPS and Network position updates
index fb7e18935954a5e6ca4f71803733e516ee06fce1..d023128f30bed382373bb85128a8a4c07984bea5 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.altusmetrum.AltosDroid;
 
+import java.util.*;
 import org.altusmetrum.altoslib_7.*;
 import android.location.Location;
 
@@ -29,16 +30,19 @@ public class TelemetryState {
        int             connect;
        DeviceAddress   address;
        AltosConfigData config;
-       AltosState      state;
        Location        location;
        int             crc_errors;
        double          frequency;
        int             telemetry_rate;
 
+       HashMap<Integer,AltosState>     states;
+
+       int             latest_serial;
+
        public TelemetryState() {
                connect = CONNECT_NONE;
                config = null;
-               state = null;
+               states = new HashMap<Integer,AltosState>();
                location = null;
                crc_errors = 0;
                frequency = AltosPreferences.frequency(0);