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 <keithp@keithp.com>
android:theme="@android:style/Theme.Dialog"
android:configChanges="orientation|keyboardHidden" />
+ <activity android:name=".SelectTrackerActivity"
+ android:label="@string/select_tracker"
+ android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" />
+
<activity android:name=".PreloadMapActivity"
android:label="@string/preload_maps"
android:theme="@android:style/Theme.Dialog"
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.Parcelable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import android.view.*;
}
}
-class Tracker implements CharSequence, Comparable {
- int serial;
- String call;
- double frequency;
-
- String display;
-
- public Tracker(int serial, String call, double frequency) {
- if (call == null)
- call = "none";
-
- this.serial = serial;
- this.call = call;
- this.frequency = frequency;
- if (frequency == 0.0)
- display = "Auto";
- else if (frequency == AltosLib.MISSING) {
- display = String.format("%-8.8s %6d", call, serial);
- } else {
- display = String.format("%-8.8s %7.3f %6d", call, frequency, serial);
- }
- }
-
- public Tracker(AltosState s) {
- this(s == null ? 0 : s.cal_data().serial,
- s == null ? null : s.cal_data().callsign,
- s == null ? 0.0 : s.frequency);
- }
-
- /* CharSequence */
- public char charAt(int index) {
- return display.charAt(index);
- }
-
- public int length() {
- return display.length();
- }
-
- public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException {
- return display.subSequence(start, end);
- }
-
- public String toString() {
- return display.toString();
- }
-
- /* Comparable */
- public int compareTo (Object other) {
- Tracker o = (Tracker) other;
- if (frequency == 0.0) {
- if (o.frequency == 0.0)
- return 0;
- return -1;
- }
- if (o.frequency == 0.0)
- return 1;
-
- int a = serial - o.serial;
- int b = call.compareTo(o.call);
- int c = (int) Math.signum(frequency - o.frequency);
-
- if (b != 0)
- return b;
- if (c != 0)
- return c;
- return a;
- }
-}
-
public class AltosDroid extends FragmentActivity implements AltosUnitsListener, LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
// Actions sent to the telemetry server at startup time
public static final int REQUEST_IDLE_MODE = 5;
public static final int REQUEST_IGNITERS = 6;
public static final int REQUEST_SETUP = 7;
+ public static final int REQUEST_SELECT_TRACKER = 8;
public static final String EXTRA_IDLE_MODE = "idle_mode";
public static final String EXTRA_IDLE_RESULT = "idle_result";
+ public static final String EXTRA_FREQUENCY = "frequency";
public static final String EXTRA_TELEMETRY_SERVICE = "telemetry_service";
+ public static final String EXTRA_TRACKERS = "trackers";
// Setup result bits
public static final int SETUP_BAUD = 1;
TelemetryState telemetry_state;
Tracker[] trackers;
-
UsbDevice pending_usb_device;
boolean start_with_usb;
case TelemetryState.CONNECT_CONNECTED:
if (telemetry_state.config != null) {
String str = String.format("S/N %d %6.3f MHz%s", telemetry_state.config.serial,
- telemetry_state.frequency, idle_mode ? " (idle)" : "");
+ telemetry_state.frequency, telemetry_state.idle_mode ? " (idle)" : "");
if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
str = str.concat(String.format(" %d bps",
AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
}
int selected_serial = 0;
- int current_serial;
long switch_time;
void set_switch_time() {
if (new_telemetry_state != null)
telemetry_state = new_telemetry_state;
- if (selected_serial != 0)
- current_serial = selected_serial;
+ if (selected_serial == 0 || telemetry_state.get(selected_serial) == null) {
+ AltosDebug.debug("selected serial set to %d", selected_serial);
+ selected_serial = telemetry_state.latest_serial;
+ }
+
+ int shown_serial = selected_serial;
- if (current_serial == 0)
- current_serial = telemetry_state.latest_serial;
+ if (telemetry_state.idle_mode)
+ shown_serial = telemetry_state.latest_serial;
if (!registered_units_listener) {
registered_units_listener = true;
}
int num_trackers = 0;
- for (AltosState s : telemetry_state.states.values()) {
+
+ for (AltosState s : telemetry_state.values()) {
num_trackers++;
}
int n = 0;
trackers[n++] = new Tracker(0, "auto", 0.0);
- for (AltosState s : telemetry_state.states.values())
+ for (AltosState s : telemetry_state.values())
trackers[n++] = new Tracker(s);
Arrays.sort(trackers);
- update_title(telemetry_state);
-
- AltosState state = null;
- boolean aged = true;
-
- if (telemetry_state.states.containsKey(current_serial)) {
- state = telemetry_state.states.get(current_serial);
- int age = state_age(state.received_time);
- if (age < 20)
- aged = false;
- if (current_serial == selected_serial)
- aged = false;
- else if (switch_time != 0 && (switch_time - state.received_time) > 0)
- aged = true;
- }
+ if (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);
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);
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));
}
}
if (resultCode == Activity.RESULT_OK)
note_setup_changes(data);
break;
+ case REQUEST_SELECT_TRACKER:
+ if (resultCode == Activity.RESULT_OK)
+ select_tracker(data);
+ break;
}
}
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();
}
}
- 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);
}
if (serial != 0) {
+ int i;
for (i = 0; i < trackers.length; i++)
if (trackers[i].serial == serial)
break;
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");
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();
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> tracker_array = new ArrayList<Tracker>(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) {
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;
}
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;
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) {
}
}
}
+
+
+ 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();
+ }
+ }
+ }
}
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);
}
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 {
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)
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));
}
}
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";
}
private String callsign() {
- return callsign.getEditableText().toString();
+ return callsignText.getEditableText().toString();
}
public void connect_idle() {
// 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() {
--- /dev/null
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import 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<Tracker> {
+ 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<Tracker> {
+ 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<Tracker> {
+ 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<Tracker> {
+ 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<Tracker> 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<Parcelable> tracker_array = (ArrayList<Parcelable>) 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;
+ }
+}
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());
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);
}
}
private void delete_serial(int serial) {
- telemetry_state.states.remove((Integer) serial);
+ telemetry_state.remove(serial);
AltosPreferences.remove_state(serial);
send_to_clients();
}
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();
}
} catch (InterruptedException ie) {
}
idle_monitor = null;
+ telemetry_state.idle_mode = false;
telemetry_start();
send_idle_mode_to_clients();
}
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);
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);
/* 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();
}
double frequency;
int telemetry_rate;
+ boolean idle_mode;
boolean quiet;
- HashMap<Integer,AltosState> states;
+ private HashMap<Integer,AltosState> 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<Integer> keySet() {
+ return states.keySet();
+ }
+
+ public Collection<AltosState> values() {
+ return states.values();
+ }
+
+ public boolean containsKey(int serial) {
+ return states.containsKey(serial);
+ }
public TelemetryState() {
connect = CONNECT_NONE;
--- /dev/null
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+import 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<Tracker> CREATOR
+ = new Parcelable.Creator<Tracker>() {
+ 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);
+ }
+}
+
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textSize="18sp"
android:padding="5dp"
-/>
\ No newline at end of file
+/>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/set_callsign_label"/>
+ <TextView android:id="@+id/frequency"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text=""
+ />
<Button android:id="@+id/connect_idle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2020 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<TableRow
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <TextView
+ android:id="@+id/call_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dp"
+ android:text="" />
+ <TextView
+ android:id="@+id/serial_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dp"
+ android:text="" />
+ <TextView
+ android:id="@+id/frequency_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dp"
+ android:text="" />
+ <TextView
+ android:id="@+id/age_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="" />
+</TableRow>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright © 2020 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <TableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tracker_list"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="1,2,3,4">
+ <TableRow
+ android:id="@+id/tracker_row"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <RadioButton
+ android:id="@+id/call_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/callsign_label" />
+ <RadioButton
+ android:id="@+id/serial_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/serial_label" />
+ <RadioButton
+ android:id="@+id/frequency_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/freq_label" />
+ <RadioButton
+ android:id="@+id/age_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/age_label" />
+ </TableRow>
+ </TableLayout>
+ </ScrollView>
+</LinearLayout>
<string name="title_other_devices">Other Available Devices</string>
<string name="button_scan">Scan for devices</string>
+ <!-- TrackerListActivity -->
+ <string name="freq_label">Freq</string>
+
<!-- Service -->
<string name="telemetry_service_label">AltosDroid Telemetry Service</string>
<string name="telemetry_service_started">Telemetry Service Started</string>
boolean remote;
boolean close_on_exit;
- double frequency;
+ double frequency = AltosLib.MISSING;
String callsign;
AltosState state;
if (state == null)
state = new AltosState(new AltosCalData(link.config_data()));
fetch.provide_data(state);
+ if (frequency != AltosLib.MISSING)
+ state.set_frequency(frequency);
if (!link.has_error && !link.reply_abort)
worked = true;
} finally {