package org.altusmetrum.AltosDroid;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Timer;
-import java.util.TimerTask;
import java.text.*;
+import java.util.*;
+import java.io.*;
import android.app.Activity;
import android.app.PendingIntent;
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.*;
+import android.graphics.*;
+import android.graphics.drawable.*;
import org.altusmetrum.altoslib_7.*;
private RelativeLayout mStateLayout;
private TextView mStateView;
private TextView mAgeView;
+ private boolean mAgeViewOld;
+ private int mAgeNewColor;
+ private int mAgeOldColor;
// field to display the version at the bottom of the screen
private TextView mVersion;
// 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;
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");
}
}
+ int selected_serial = 0;
+ int current_serial;
+ long switch_time;
+
+ void set_switch_time() {
+ switch_time = System.currentTimeMillis();
+ selected_serial = 0;
+ }
+
boolean registered_units_listener;
- void update_state(TelemetryState telemetry_state) {
+ void update_state(TelemetryState new_telemetry_state) {
+
+ if (new_telemetry_state != null)
+ telemetry_state = new_telemetry_state;
+
+ if (selected_serial != 0)
+ current_serial = selected_serial;
+
+ 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]);
+ Arrays.sort(serials);
+
update_title(telemetry_state);
- update_ui(telemetry_state.state, telemetry_state.location);
- if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
- start_timer();
+
+ 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);
+ 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 (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);
+
+ if (newest_state == null || existing_age < newest_age) {
+ newest_state = existing;
+ newest_age = existing_age;
+ }
+ }
+
+ if (newest_state != null)
+ state = newest_state;
+ }
+
+ update_ui(telemetry_state, state, telemetry_state.location);
+
+ start_timer();
}
boolean same_string(String a, String b) {
}
}
+
+ 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)
- mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+ if (saved_state != null) {
+ 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;
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;
if (prev_locked != locked) {
String currentTab = mTabHost.getCurrentTabTag();
if (locked) {
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("flight");
} else {
- if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad");
+ if (currentTab.equals("flight")) mTabHost.setCurrentTabByTag("pad");
}
}
} else {
String currentTab = mTabHost.getCurrentTabTag();
switch (state.state) {
case AltosLib.ao_flight_boost:
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
- break;
- case AltosLib.ao_flight_drogue:
- if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("flight");
break;
case AltosLib.ao_flight_landed:
- if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
+ if (currentTab.equals("flight")) mTabHost.setCurrentTabByTag("recover");
break;
case AltosLib.ao_flight_stateless:
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("flight");
break;
}
}
}
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);
return tab_view;
}
+ public void set_map_source(int source) {
+ for (AltosDroidTab mTab : mTabs)
+ mTab.set_map_source(source);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AltosDebug.init(this);
AltosDebug.debug("+++ ON CREATE +++");
+ // Initialise preferences
+ AltosDroidPreferences.init(this);
+
fm = getSupportFragmentManager();
// Set up the window layout
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter.addTab(mTabHost.newTabSpec("pad").setIndicator(create_tab_view("Pad")), TabPad.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("ascent").setIndicator(create_tab_view("Ascent")), TabAscent.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("descent").setIndicator(create_tab_view("Descent")), TabDescent.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("landed").setIndicator(create_tab_view("Landed")), TabLanded.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec("flight").setIndicator(create_tab_view("Flight")), TabFlight.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec("recover").setIndicator(create_tab_view("Recover")), TabRecover.class, null);
mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator(create_tab_view("Map")), TabMap.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("offmap").setIndicator(create_tab_view("OffMap")), TabMapOffline.class, null);
// Display the Version
mVersion = (TextView) findViewById(R.id.version);
mStateLayout = (RelativeLayout) findViewById(R.id.state_container);
mStateView = (TextView) findViewById(R.id.state_value);
mAgeView = (TextView) findViewById(R.id.age_value);
+ mAgeNewColor = mAgeView.getTextColors().getDefaultColor();
+ mAgeOldColor = getResources().getColor(R.color.old_color);
}
private boolean ensureBluetooth() {
void setFrequency(double freq) {
try {
mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
+ set_switch_time();
} catch (RemoteException e) {
}
}
void setBaud(int baud) {
try {
mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
+ set_switch_time();
} catch (RemoteException e) {
}
}
}
}
+ void select_tracker(int serial) {
+ int i;
+
+ AltosDebug.debug("select tracker %d\n", serial);
+
+ if (serial == selected_serial) {
+ AltosDebug.debug("%d already selected\n", serial);
+ return;
+ }
+
+ if (serial != 0) {
+ for (i = 0; i < serials.length; i++)
+ if (serials[i] == serial)
+ break;
+
+ if (i == serials.length) {
+ AltosDebug.debug("attempt to select unknown tracker %d\n", serial);
+ return;
+ }
+ }
+
+ current_serial = selected_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;
serverIntent = new Intent(this, MapTypeActivity.class);
startActivityForResult(serverIntent, REQUEST_MAP_TYPE);
return true;
+ case R.id.map_source:
+ int source = AltosDroidPreferences.map_source();
+ int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE;
+ AltosDroidPreferences.set_map_source(new_source);
+ set_map_source(new_source);
+ return true;
+ case R.id.select_tracker:
+ if (serials != null) {
+ String[] trackers = new String[serials.length+1];
+ trackers[0] = "Auto";
+ for (int i = 0; i < serials.length; i++)
+ trackers[i+1] = 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) {
+ if (item == 0)
+ select_tracker(0);
+ else
+ select_tracker(serials[item-1]);
+ }
+ });
+ 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;
}
+ static String direction(AltosGreatCircle from_receiver,
+ Location receiver) {
+ if (!receiver.hasBearing())
+ return null;
+
+ float bearing = receiver.getBearing();
+ float heading = (float) from_receiver.bearing - bearing;
+
+ while (heading <= -180.0f)
+ heading += 360.0f;
+ while (heading > 180.0f)
+ heading -= 360.0f;
+
+ int iheading = (int) (heading + 0.5f);
+
+ if (-1 < iheading && iheading < 1)
+ return "ahead";
+ else if (iheading < -179 || 179 < iheading)
+ return "backwards";
+ else if (iheading < 0)
+ return String.format("left %d", -iheading);
+ else
+ return String.format("right %d", iheading);
+ }
}