From 79f2677143f85a2807b8d4e297f3617aafbd34da Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 8 Feb 2020 20:44:10 -0800 Subject: [PATCH] altosdroid: Change tracker selection dialog Create a table of trackers and allow sorting based on each column. When a tracker is selected, the app will not change to another tracker automatically. Signed-off-by: Keith Packard --- .../app/src/main/AndroidManifest.xml.in | 5 + .../altusmetrum/AltosDroid/AltosDroid.java | 204 ++++-------- .../AltosDroid/AltosDroidPreferences.java | 24 ++ .../AltosDroid/AltosMapOffline.java | 8 +- .../AltosDroid/AltosMapOnline.java | 6 +- .../AltosDroid/IdleModeActivity.java | 14 +- .../AltosDroid/SelectTrackerActivity.java | 294 ++++++++++++++++++ .../AltosDroid/TelemetryService.java | 23 +- .../AltosDroid/TelemetryState.java | 35 ++- .../org/altusmetrum/AltosDroid/Tracker.java | 201 ++++++++++++ .../app/src/main/res/layout/device_name.xml | 3 +- .../app/src/main/res/layout/idle_mode.xml | 5 + .../app/src/main/res/layout/tracker_ent.xml | 52 ++++ .../app/src/main/res/layout/tracker_list.xml | 68 ++++ .../app/src/main/res/values/strings.xml | 3 + altoslib/AltosIdleMonitor.java | 4 +- 16 files changed, 781 insertions(+), 168 deletions(-) create mode 100644 altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SelectTrackerActivity.java create mode 100644 altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Tracker.java create mode 100644 altosdroid/app/src/main/res/layout/tracker_ent.xml create mode 100644 altosdroid/app/src/main/res/layout/tracker_list.xml diff --git a/altosdroid/app/src/main/AndroidManifest.xml.in b/altosdroid/app/src/main/AndroidManifest.xml.in index d237af4d..efedddaf 100644 --- a/altosdroid/app/src/main/AndroidManifest.xml.in +++ b/altosdroid/app/src/main/AndroidManifest.xml.in @@ -68,6 +68,11 @@ android:theme="@android:style/Theme.Dialog" android:configChanges="orientation|keyboardHidden" /> + + 0) - aged = true; - } + if (telemetry_state.frequency != AltosLib.MISSING) + telem_frequency = telemetry_state.frequency; - if (aged) { - AltosState newest_state = null; - int newest_age = 0; - - for (int serial : telemetry_state.states.keySet()) { - AltosState existing = telemetry_state.states.get(serial); - int existing_age = state_age(existing.received_time); - - if (newest_state == null || existing_age < newest_age) { - newest_state = existing; - newest_age = existing_age; - } - } + update_title(telemetry_state); - if (newest_state != null) - state = newest_state; - } + AltosState state = telemetry_state.get(shown_serial); update_ui(telemetry_state, state, telemetry_state.quiet); @@ -477,6 +388,19 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + static String age_string(int 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)); + return text; + } + void update_age() { if (saved_state != null) { int age = state_age(saved_state.received_time); @@ -490,16 +414,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, 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); + mAgeView.setText(age_string(age)); } } @@ -950,6 +865,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, if (resultCode == Activity.RESULT_OK) note_setup_changes(data); break; + case REQUEST_SELECT_TRACKER: + if (resultCode == Activity.RESULT_OK) + select_tracker(data); + break; } } @@ -1069,7 +988,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, return true; } + double telem_frequency = 434.550; + void setFrequency(double freq) { + telem_frequency = freq; try { mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq)); set_switch_time(); @@ -1109,10 +1031,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, } } - void select_tracker(int serial) { - int i; + void select_tracker(int serial, double frequency) { - AltosDebug.debug("select tracker %d\n", serial); + AltosDebug.debug("select tracker %d %7.3f\n", serial, frequency); if (serial == selected_serial) { AltosDebug.debug("%d already selected\n", serial); @@ -1120,6 +1041,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, } if (serial != 0) { + int i; for (i = 0; i < trackers.length; i++) if (trackers[i].serial == serial) break; @@ -1128,12 +1050,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, AltosDebug.debug("attempt to select unknown tracker %d\n", serial); return; } + if (frequency != 0.0 && frequency != AltosLib.MISSING) + setFrequency(frequency); } - current_serial = selected_serial = serial; + selected_serial = serial; update_state(null); } + void select_tracker(Intent data) { + int serial = data.getIntExtra(SelectTrackerActivity.EXTRA_SERIAL_NUMBER, 0); + double frequency = data.getDoubleExtra(SelectTrackerActivity.EXTRA_FREQUENCY, 0.0); + select_tracker(serial, frequency); + } + void touch_trackers(Integer[] serials) { AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this); builder_tracker.setTitle("Select Tracker"); @@ -1143,16 +1073,16 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, my_trackers[0] = new Tracker(null); for (int i = 0; i < serials.length; i++) { - AltosState s = telemetry_state.states.get(serials[i]); + AltosState s = telemetry_state.get(serials[i]); my_trackers[i+1] = new Tracker(s); } builder_tracker.setItems(my_trackers, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if (item == 0) - select_tracker(0); + select_tracker(0, 0.0); else - select_tracker(my_trackers[item].serial); + select_tracker(my_trackers[item].serial, my_trackers[item].frequency); } }); AlertDialog alert_tracker = builder_tracker.create(); @@ -1210,23 +1140,14 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, alert_freq.show(); return true; case R.id.select_tracker: + serverIntent = new Intent(this, SelectTrackerActivity.class); if (trackers != null) { - AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); - builder_serial.setTitle("Select a tracker"); - builder_serial.setItems(trackers, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int item) { - System.out.printf("select item %d %s\n", item, trackers[item].display); - if (item == 0) - select_tracker(0); - else - select_tracker(trackers[item].serial); - } - }); - AlertDialog alert_serial = builder_serial.create(); - alert_serial.show(); - + ArrayList tracker_array = new ArrayList(Arrays.asList(trackers)); + serverIntent.putParcelableArrayListExtra(EXTRA_TRACKERS, tracker_array); + } else { + serverIntent.putExtra(EXTRA_TRACKERS, (Parcelable[]) null); } + startActivityForResult(serverIntent, REQUEST_SELECT_TRACKER); return true; case R.id.delete_track: if (trackers != null) { @@ -1249,6 +1170,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, case R.id.idle_mode: serverIntent = new Intent(this, IdleModeActivity.class); serverIntent.putExtra(EXTRA_IDLE_MODE, idle_mode); + serverIntent.putExtra(EXTRA_FREQUENCY, telem_frequency); startActivityForResult(serverIntent, REQUEST_IDLE_MODE); return true; } diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index f2395568..876abcc4 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -47,6 +47,11 @@ public class AltosDroidPreferences extends AltosPreferences { static int map_source; + /* Tracker sort selection */ + final static String trackerSortPreference = "TRACKER-SORT"; + + static int tracker_sort; + public static void init(Context context) { if (backend != null) return; @@ -62,6 +67,8 @@ public class AltosDroidPreferences extends AltosPreferences { active_device_address = new DeviceAddress (address, name); map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE); + + tracker_sort = backend.getInt(trackerSortPreference, 0); } public static void set_active_device(DeviceAddress address) { @@ -134,4 +141,21 @@ public class AltosDroidPreferences extends AltosPreferences { } } } + + + public static int tracker_sort() { + synchronized(backend) { + return tracker_sort; + } + } + + public static void set_tracker_sort(int new_tracker_sort) { + synchronized(backend) { + if (tracker_sort != new_tracker_sort) { + tracker_sort = new_tracker_sort; + backend.putInt(trackerSortPreference, tracker_sort); + flush_preferences(); + } + } + } } diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java index a2b0b25b..822f1f79 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -437,11 +437,11 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal if (telem_state != null) { Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); - Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); + Integer[] new_serial = telem_state.keySet().toArray(new Integer[0]); /* remove deleted keys */ for (int serial : old_serial) { - if (!telem_state.states.containsKey(serial)) + if (!telem_state.containsKey(serial)) rockets.remove(serial); } @@ -449,7 +449,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal for (int serial : new_serial) { Rocket rocket; - AltosState t_state = telem_state.states.get(serial); + AltosState t_state = telem_state.get(serial); if (rockets.containsKey(serial)) rocket = rockets.get(serial); else { @@ -459,7 +459,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal if (t_state.gps != null) { AltosLatLon latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon); rocket.set_position(latlon, t_state.received_time); - if (state.cal_data().serial == serial) + if (state != null && state.cal_data().serial == serial) there = latlon; } if (state != null) diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java index 3ebae8fe..8361b278 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -287,12 +287,12 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke if (telem_state != null) { for (int serial : rockets.keySet()) { - if (!telem_state.states.containsKey(serial)) + if (!telem_state.containsKey(serial)) remove_rocket(serial); } - for (int serial : telem_state.states.keySet()) { - set_rocket(serial, telem_state.states.get(serial)); + for (int serial : telem_state.keySet()) { + set_rocket(serial, telem_state.get(serial)); } } diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java index 9c786ac4..763ba3f1 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/IdleModeActivity.java @@ -29,11 +29,13 @@ import android.widget.*; import org.altusmetrum.altoslib_13.*; public class IdleModeActivity extends Activity { - private EditText callsign; + private EditText callsignText; + private TextView frequencyView; private Button connect; private Button disconnect; private Button reboot; private Button igniters; + private double frequency; public static final String EXTRA_IDLE_MODE = "idle_mode"; public static final String EXTRA_IDLE_RESULT = "idle_result"; @@ -52,7 +54,7 @@ public class IdleModeActivity extends Activity { } private String callsign() { - return callsign.getEditableText().toString(); + return callsignText.getEditableText().toString(); } public void connect_idle() { @@ -80,8 +82,12 @@ public class IdleModeActivity extends Activity { // Setup the window setContentView(R.layout.idle_mode); - callsign = (EditText) findViewById(R.id.set_callsign); - callsign.setText(new StringBuffer(AltosPreferences.callsign())); + callsignText = (EditText) findViewById(R.id.set_callsign); + callsignText.setText(new StringBuffer(AltosPreferences.callsign())); + + frequency = getIntent().getDoubleExtra(AltosDroid.EXTRA_FREQUENCY, 0.0); + frequencyView = (TextView) findViewById(R.id.frequency); + frequencyView.setText(String.format("Frequency: %7.3f MHz", frequency)); connect = (Button) findViewById(R.id.connect_idle); connect.setOnClickListener(new OnClickListener() { diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SelectTrackerActivity.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SelectTrackerActivity.java new file mode 100644 index 00000000..aa527562 --- /dev/null +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/SelectTrackerActivity.java @@ -0,0 +1,294 @@ +/* + * Copyright © 2020 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import android.app.Activity; +import android.content.*; +import android.os.*; +import android.util.*; +import android.view.*; +import android.view.View.*; +import android.widget.*; +import android.graphics.*; +import android.graphics.drawable.*; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import org.altusmetrum.altoslib_13.*; + +class TrackerComparatorCall implements Comparator { + public int compare(Tracker a, Tracker b) { + int v; + + v = a.compareCall(b); + if (v != 0) + return v; + v = a.compareAge(b); + if (v != 0) + return v; + v = a.compareSerial(b); + if (v != 0) + return v; + return a.compareFrequency(b); + } + public boolean equals(Object o) { + return o instanceof TrackerComparatorCall; + } +} + +class TrackerComparatorSerial implements Comparator { + public int compare(Tracker a, Tracker b) { + int v; + + v = a.compareSerial(b); + if (v != 0) + return v; + v = a.compareAge(b); + if (v != 0) + return v; + v = a.compareCall(b); + if (v != 0) + return v; + return a.compareFrequency(b); + } + public boolean equals(Object o) { + return o instanceof TrackerComparatorSerial; + } +} + +class TrackerComparatorAge implements Comparator { + public int compare(Tracker a, Tracker b) { + int v; + + v = a.compareAge(b); + if (v != 0) + return v; + v = a.compareCall(b); + if (v != 0) + return v; + v = a.compareSerial(b); + if (v != 0) + return v; + return a.compareFrequency(b); + } + public boolean equals(Object o) { + return o instanceof TrackerComparatorAge; + } +} + +class TrackerComparatorFrequency implements Comparator { + public int compare(Tracker a, Tracker b) { + int v; + + v = a.compareFrequency(b); + if (v != 0) + return v; + v = a.compareAge(b); + if (v != 0) + return v; + v = a.compareCall(b); + if (v != 0) + return v; + return a.compareSerial(b); + } + public boolean equals(Object o) { + return o instanceof TrackerComparatorFrequency; + } +} + +public class SelectTrackerActivity extends Activity implements OnTouchListener { + // Return Intent extra + public static final String EXTRA_SERIAL_NUMBER = "serial_number"; + public static final String EXTRA_FREQUENCY = "frequency"; + + private int button_ids[] = { + R.id.call_button, + R.id.serial_button, + R.id.frequency_button, + R.id.age_button + }; + + private static final int call_button = 0; + private static final int serial_button = 1; + private static final int freq_button = 2; + private static final int age_button = 3; + private RadioButton radio_buttons[] = new RadioButton[4]; + private TableLayout table; + + private Tracker[] trackers; + + private void set_sort(int id) { + AltosDroidPreferences.set_tracker_sort(id); + resort(); + } + + private void resort() { + Comparator compare; + int tracker_sort = AltosDroidPreferences.tracker_sort(); + AltosDebug.debug("sort %d", tracker_sort); + switch (tracker_sort) { + case call_button: + default: + compare = new TrackerComparatorCall(); + break; + case serial_button: + compare = new TrackerComparatorSerial(); + break; + case freq_button: + compare = new TrackerComparatorFrequency(); + break; + case age_button: + compare = new TrackerComparatorAge(); + break; + } + Arrays.sort(trackers, compare); + set_trackers(); + } + + void init_button_state() { + int tracker_sort = AltosDroidPreferences.tracker_sort(); + for (int i = 0; i < 4; i++) + radio_buttons[i].setChecked(i == tracker_sort); + } + + OnCheckedChangeListener button_listener = new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + int id = buttonView.getId(); + if (isChecked) { + int sort_id = -1; + for (int i = 0; i < 4; i++) { + if (id == button_ids[i]) + sort_id = i; + else + radio_buttons[i].setChecked(false); + } + if (sort_id != -1) + set_sort(sort_id); + } + } + }; + + long start_time; + + private void + insert_tracker(Tracker tracker) { + TableRow row = (TableRow) getLayoutInflater().inflate(R.layout.tracker_ent, null); + + ((TextView) row.findViewById(R.id.call_view)).setText(tracker.call); + if (tracker.serial == 0) + ((TextView) row.findViewById(R.id.serial_view)).setText(""); + else + ((TextView) row.findViewById(R.id.serial_view)).setText(String.format("%d", tracker.serial)); + if (tracker.frequency == 0.0) + ((TextView) row.findViewById(R.id.frequency_view)).setText(""); + else if (tracker.frequency == AltosLib.MISSING) + ((TextView) row.findViewById(R.id.frequency_view)).setText(""); + else + ((TextView) row.findViewById(R.id.frequency_view)).setText(String.format("%7.3f", tracker.frequency)); + if (tracker.received_time != 0) { + int age = (int) ((start_time - tracker.received_time + 500) / 1000); + ((TextView) row.findViewById(R.id.age_view)).setText(AltosDroid.age_string(age)); + } else { + ((TextView) row.findViewById(R.id.age_view)).setText(""); + } + row.setClickable(true); + row.setOnTouchListener(this); + table.addView(row); + } + + private void set_trackers() { + for (int i = table.getChildCount() - 1; i >= 1; i--) + table.removeViewAt(i); + for (Tracker tracker : trackers) + insert_tracker(tracker); + } + + private void done(View v) { + int result = Activity.RESULT_CANCELED; + Intent intent = new Intent(); + for (int i = 1; i < table.getChildCount(); i++) { + View child = table.getChildAt(i); + if (child == v) { + Tracker tracker = trackers[i - 1]; + intent.putExtra(EXTRA_SERIAL_NUMBER, tracker.serial); + intent.putExtra(EXTRA_FREQUENCY, tracker.frequency); + result = Activity.RESULT_OK; + break; + } + } + setResult(Activity.RESULT_OK, intent); + finish(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]); + super.onCreate(savedInstanceState); + + setContentView(R.layout.tracker_list); + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + + for (int i = 0; i < 4; i++) { + radio_buttons[i] = (RadioButton) findViewById(button_ids[i]); + radio_buttons[i].setOnCheckedChangeListener(button_listener); + } + + ArrayList tracker_array = (ArrayList) getIntent().getParcelableArrayListExtra(AltosDroid.EXTRA_TRACKERS); + if (tracker_array != null) { + Object[] array = tracker_array.toArray(); + trackers = new Tracker[array.length]; + for (int i = 0; i < array.length; i++) + trackers[i] = (Tracker) array[i]; + } + + start_time = System.currentTimeMillis(); + + table = (TableLayout) findViewById(R.id.tracker_list); + + init_button_state(); + + resort(); + + set_trackers(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + switch (action) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_OUTSIDE: + v.setBackgroundColor(0); + v.invalidate(); + break; + case MotionEvent.ACTION_DOWN: + v.setBackgroundColor(Color.RED); + v.invalidate(); + break; + } + if (action == MotionEvent.ACTION_UP) { + done(v); + return true; + } + return false; + } +} diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java index d4f72b3f..2bec95bc 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java @@ -251,12 +251,11 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene private void telemetry(AltosTelemetry telem) { AltosState state; - if (telemetry_state.states.containsKey(telem.serial())) - state = telemetry_state.states.get(telem.serial()); - else + state = telemetry_state.get(telem.serial()); + if (state == null) state = new AltosState(new AltosCalData()); telem.provide_data(state); - telemetry_state.states.put(telem.serial(), state); + telemetry_state.put(telem.serial(), state); telemetry_state.quiet = false; if (state != null) { AltosPreferences.set_state(state,telem.serial()); @@ -269,8 +268,6 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene private Message message() { if (telemetry_state == null) AltosDebug.debug("telemetry_state null!"); - if (telemetry_state.states == null) - AltosDebug.debug("telemetry_state.states null!"); return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state); } @@ -411,7 +408,7 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene } private void delete_serial(int serial) { - telemetry_state.states.remove((Integer) serial); + telemetry_state.remove(serial); AltosPreferences.remove_state(serial); send_to_clients(); } @@ -438,6 +435,8 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene telemetry_stop(); idle_monitor = new AltosIdleMonitor(this, altos_link, true, false); idle_monitor.set_callsign(AltosPreferences.callsign()); + idle_monitor.set_frequency(telemetry_state.frequency); + telemetry_state.idle_mode = true; idle_monitor.start(); send_idle_mode_to_clients(); } @@ -450,6 +449,7 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene } catch (InterruptedException ie) { } idle_monitor = null; + telemetry_state.idle_mode = false; telemetry_start(); send_idle_mode_to_clients(); } @@ -615,9 +615,6 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene for (int serial : serials) { AltosState saved_state = AltosPreferences.state(serial); if (saved_state != null) { - if (telemetry_state.latest_serial == 0) - telemetry_state.latest_serial = serial; - AltosDebug.debug("recovered old state serial %d flight %d", serial, saved_state.cal_data().flight); @@ -625,7 +622,7 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene AltosDebug.debug("\tposition %f,%f", saved_state.gps.lat, saved_state.gps.lon); - telemetry_state.states.put(serial, saved_state); + telemetry_state.put(serial, saved_state); } else { AltosDebug.debug("Failed to recover state for %d", serial); AltosPreferences.remove_state(serial); @@ -710,7 +707,9 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene /* AltosIdleMonitorListener */ public void update(AltosState state, AltosListenerState listener_state) { - telemetry_state.states.put(state.cal_data().serial, state); + if (state != null) + AltosDebug.debug("update call %s freq %7.3f", state.cal_data().callsign, state.frequency); + telemetry_state.put(state.cal_data().serial, state); telemetry_state.receiver_battery = listener_state.battery; send_to_clients(); } diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java index d292c7bf..4ce644e5 100644 --- a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryState.java @@ -35,11 +35,44 @@ public class TelemetryState { double frequency; int telemetry_rate; + boolean idle_mode; boolean quiet; - HashMap states; + private HashMap states; int latest_serial; + long latest_received_time; + + public void put(int serial, AltosState state) { + long received_time = state.received_time; + if (received_time > latest_received_time || latest_serial == 0) { + latest_serial = serial; + latest_received_time = received_time; + } + states.put(serial, state); + } + + public AltosState get(int serial) { + if (states.containsKey(serial)) + return states.get(serial); + return null; + } + + public void remove(int serial) { + states.remove((Integer) serial); + } + + public Set keySet() { + return states.keySet(); + } + + public Collection values() { + return states.values(); + } + + public boolean containsKey(int serial) { + return states.containsKey(serial); + } public TelemetryState() { connect = CONNECT_NONE; diff --git a/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Tracker.java b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Tracker.java new file mode 100644 index 00000000..72042e65 --- /dev/null +++ b/altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/Tracker.java @@ -0,0 +1,201 @@ +/* + * Copyright © 2020 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.lang.ref.WeakReference; +import java.util.*; + +import android.Manifest; +import android.app.Activity; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.content.Intent; +import android.content.Context; +import android.content.ComponentName; +import android.content.ServiceConnection; +import android.content.DialogInterface; +import android.os.IBinder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.os.Parcelable; +import android.os.Parcel; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import android.view.*; +import android.widget.*; +import android.app.AlertDialog; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationListener; +import android.hardware.usb.*; +import android.content.pm.PackageManager; +import androidx.core.app.ActivityCompat; +import org.altusmetrum.altoslib_13.*; + +public class Tracker implements CharSequence, Comparable, Parcelable { + int serial; + String call; + double frequency; + long received_time; + String display; + + private void make_display() { + if (frequency == 0.0) + display = "Auto"; + else if (frequency == AltosLib.MISSING) { + display = String.format("%-8.8s %6d", call, serial); + } else { + display = String.format("%-8.8s %7.3f %6d", call, frequency, serial); + } + } + + public Tracker(int serial, String call, double frequency, long received_time) { + if (call == null) + call = "none"; + + this.serial = serial; + this.call = call; + this.frequency = frequency; + this.received_time = received_time; + make_display(); + } + + public Tracker(int serial, String call, double frequency) { + this(serial, call, frequency, 0); + } + + public Tracker(AltosState s) { + this(s == null ? 0 : s.cal_data().serial, + s == null ? null : s.cal_data().callsign, + s == null ? 0.0 : s.frequency, + s == null ? 0 : s.received_time); + } + + /* CharSequence */ + public char charAt(int index) { + return display.charAt(index); + } + + public int length() { + return display.length(); + } + + public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException { + return display.subSequence(start, end); + } + + public String toString() { + return display.toString(); + } + + /* Comparable */ + public int compareTo (Object other) { + Tracker o = (Tracker) other; + if (frequency == 0.0) { + if (o.frequency == 0.0) + return 0; + return -1; + } + if (o.frequency == 0.0) + return 1; + + int a = serial - o.serial; + int b = call.compareTo(o.call); + int c = (int) Math.signum(frequency - o.frequency); + + if (b != 0) + return b; + if (c != 0) + return c; + return a; + } + + /* Parcelable */ + + public int describeContents() { + AltosDebug.debug("describe contents %d", serial); + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + AltosDebug.debug("write to parcel %s", display); + out.writeInt(serial); + out.writeString(call); + out.writeDouble(frequency); + out.writeLong(received_time); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public Tracker createFromParcel(Parcel in) { + AltosDebug.debug("createFromParcel"); + return new Tracker(in); + } + + public Tracker[] newArray(int size) { + AltosDebug.debug("newArray %d", size); + return new Tracker[size]; + } + }; + + /* newer (-1), same (0), older(1) */ + public int compareAge(Tracker o) { + if (received_time == o.received_time) + return 0; + if (received_time == 0) + return -1; + if (o.received_time == 0) + return 1; + if (received_time > o.received_time) + return -1; + return 1; + } + + public int compareCall(Tracker o) { + int v = call.compareTo(o.call); + if (v == 0) + return v; + if (call.equals("auto")) + return -1; + if (o.call.equals("auto")) + return 1; + return v; + } + + public int compareSerial(Tracker o) { + return serial - o.serial; + } + + public int compareFrequency(Tracker o) { + return (int) Math.signum(frequency - o.frequency); + } + + private Tracker(Parcel in) { + serial = in.readInt(); + call = in.readString(); + frequency = in.readDouble(); + received_time = in.readLong(); + make_display(); + AltosDebug.debug("Create from parcel %s", display); + } +} + diff --git a/altosdroid/app/src/main/res/layout/device_name.xml b/altosdroid/app/src/main/res/layout/device_name.xml index 8fa358c9..fa09eaa3 100644 --- a/altosdroid/app/src/main/res/layout/device_name.xml +++ b/altosdroid/app/src/main/res/layout/device_name.xml @@ -16,6 +16,5 @@ \ No newline at end of file +/> diff --git a/altosdroid/app/src/main/res/layout/idle_mode.xml b/altosdroid/app/src/main/res/layout/idle_mode.xml index 6c598c1c..11b5528b 100644 --- a/altosdroid/app/src/main/res/layout/idle_mode.xml +++ b/altosdroid/app/src/main/res/layout/idle_mode.xml @@ -32,6 +32,11 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/set_callsign_label"/> +