X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=altosdroid%2Fsrc%2Forg%2Faltusmetrum%2FAltosDroid%2FAltosDroid.java;h=db1ca6916c643fc448ad69639e860fdf8e0f058c;hp=3396f77e40f9df866d94bd5ef87e355cfa1a3299;hb=bc3fbcb35090be3856284ccf4d908ebf39d02bec;hpb=fe8ab96e8727c25c04cac473cafb264cf7e80156 diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 3396f77e..db1ca691 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Mike Beattie + * Copyright © 2012-2013 Mike Beattie * * 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 @@ -18,6 +18,9 @@ package org.altusmetrum.AltosDroid; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; import android.app.Activity; import android.bluetooth.BluetoothAdapter; @@ -33,34 +36,44 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.text.method.ScrollingMovementMethod; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.Window; +import android.view.View; +import android.widget.TabHost; import android.widget.TextView; +import android.widget.RelativeLayout; import android.widget.Toast; import android.app.AlertDialog; +import android.location.Location; -import org.altusmetrum.AltosLib.*; +import org.altusmetrum.altoslib_5.*; -/** - * This is the main Activity that displays the current chat session. - */ -public class AltosDroid extends Activity { +public class AltosDroid extends FragmentActivity { // Debugging - private static final String TAG = "AltosDroid"; - private static final boolean D = true; + static final String TAG = "AltosDroid"; + static final boolean D = true; // Message types received by our Handler public static final int MSG_STATE_CHANGE = 1; public static final int MSG_TELEMETRY = 2; + public static final int MSG_UPDATE_AGE = 3; + public static final int MSG_LOCATION = 4; + public static final int MSG_CRC_ERROR = 5; + public static final int MSG_FREQUENCY = 6; + public static final int MSG_TELEMETRY_RATE = 7; // Intent request codes private static final int REQUEST_CONNECT_DEVICE = 1; private static final int REQUEST_ENABLE_BT = 2; + public static FragmentManager fm; + // Layout Views private TextView mTitle; @@ -69,27 +82,33 @@ public class AltosDroid extends Activity { private TextView mRSSIView; private TextView mSerialView; private TextView mFlightView; + private RelativeLayout mStateLayout; private TextView mStateView; - private TextView mSpeedView; - private TextView mAccelView; - private TextView mRangeView; - private TextView mHeightView; - private TextView mElevationView; - private TextView mBearingView; - private TextView mLatitudeView; - private TextView mLongitudeView; - - // Generic field for extras at the bottom - private TextView mTextView; + private TextView mAgeView; + + // field to display the version at the bottom of the screen + private TextView mVersion; + + private double frequency; + private int telemetry_rate; + + // Tabs + TabHost mTabHost; + AltosViewPager mViewPager; + TabsAdapter mTabsAdapter; + ArrayList mTabs = new ArrayList(); + int tabHeight; + + // Timer and Saved flight state for Age calculation + private Timer timer = new Timer(); + AltosState saved_state; + Location saved_location; // Service private boolean mIsBound = false; private Messenger mService = null; final Messenger mMessenger = new Messenger(new IncomingHandler(this)); - // Preferences - private AltosDroidPreferences prefs = null; - // TeleBT Config data private AltosConfigData mConfigData = null; // Local Bluetooth adapter @@ -111,14 +130,7 @@ public class AltosDroid extends Activity { if(D) Log.d(TAG, "MSG_STATE_CHANGE: " + msg.arg1); switch (msg.arg1) { case TelemetryService.STATE_CONNECTED: - ad.mConfigData = (AltosConfigData) msg.obj; - String str = String.format(" %s S/N: %d", ad.mConfigData.product, ad.mConfigData.serial); - ad.mTitle.setText(R.string.title_connected_to); - ad.mTitle.append(str); - Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show(); - ad.mAltosVoice.speak("Connected"); - //TEST! - ad.mTextView.setText(Dumper.dump(ad.mConfigData)); + ad.set_config_data((AltosConfigData) msg.obj); break; case TelemetryService.STATE_CONNECTING: ad.mTitle.setText(R.string.title_connecting); @@ -127,14 +139,30 @@ public class AltosDroid extends Activity { case TelemetryService.STATE_NONE: ad.mConfigData = null; ad.mTitle.setText(R.string.title_not_connected); - ad.mTextView.setText(""); + String active_device = AltosDroidPreferences.active_device(); + if (active_device != null) + ad.connectDevice(active_device); break; } break; case MSG_TELEMETRY: ad.update_ui((AltosState) msg.obj); - // TEST! - ad.mTextView.setText(Dumper.dump(msg.obj)); + break; + case MSG_LOCATION: + ad.set_location((Location) msg.obj); + break; + case MSG_CRC_ERROR: + break; + case MSG_UPDATE_AGE: + if (ad.saved_state != null) { + ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.received_time + 500) / 1000)); + } + break; + case MSG_FREQUENCY: + ad.set_frequency((Double) msg.obj); + break; + case MSG_TELEMETRY_RATE: + ad.set_telemetry_rate((Integer) msg.obj); break; } } @@ -159,7 +187,6 @@ public class AltosDroid extends Activity { } }; - void doBindService() { bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; @@ -183,37 +210,171 @@ public class AltosDroid extends Activity { } } + public void registerTab(AltosDroidTab mTab) { + mTabs.add(mTab); + } + + public void unregisterTab(AltosDroidTab mTab) { + mTabs.remove(mTab); + } + + void set_location(Location location) { + saved_location = location; + Log.d(TAG, "set_location"); + update_ui(saved_state); + } + + void set_title() { + if (mConfigData != null) { + String str = String.format("S/N %d %6.3f MHz", mConfigData.serial, frequency); + + if (telemetry_rate != AltosLib.ao_telemetry_rate_38400) + str = str.concat(String.format(" %d bps", AltosLib.ao_telemetry_rate_values[telemetry_rate])); + mTitle.setText(str); + } + } + + void set_frequency(double frequency) { + if (D) Log.d(TAG, String.format("AltosDroid: set_frequency %f\n", frequency)); + this.frequency = frequency; + set_title(); + } + + void set_telemetry_rate(int telemetry_rate) { + if (D) Log.d(TAG, String.format("AltosDroid: set_telemetry_rate %d\n", telemetry_rate)); + this.telemetry_rate = telemetry_rate; + set_title(); + } + + void set_config_data(AltosConfigData config_data) { + mConfigData = config_data; + set_title(); + } + + boolean same_string(String a, String b) { + if (a == null) { + if (b == null) + return true; + return false; + } else { + if (b == null) + return false; + return a.equals(b); + } + } + void update_ui(AltosState state) { - mCallsignView.setText(state.data.callsign); - mRSSIView.setText(String.format("%d", state.data.rssi)); - mSerialView.setText(String.format("%d", state.data.serial)); - mFlightView.setText(String.format("%d", state.data.flight)); - mStateView.setText(state.data.state()); - double speed = state.speed; - if (!state.ascent) - speed = state.baro_speed; - mSpeedView.setText(String.format("%6.0f m/s", speed)); - mAccelView.setText(String.format("%6.0f m/s²", state.acceleration)); - mRangeView.setText(String.format("%6.0f m", state.range)); - mHeightView.setText(String.format("%6.0f m", state.height)); - mElevationView.setText(String.format("%3.0f°", state.elevation)); - if (state.from_pad != null) - mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing)); - mLatitudeView.setText(pos(state.gps.lat, "N", "S")); - mLongitudeView.setText(pos(state.gps.lon, "W", "E")); - - mAltosVoice.tell(state); + + Log.d(TAG, "update_ui"); + + int prev_state = AltosLib.ao_flight_invalid; + + if (saved_state != null) + prev_state = saved_state.state; + + if (state != null) { + Log.d(TAG, String.format("prev state %d new state %d\n", prev_state, state.state)); + if (prev_state != state.state) { + String currentTab = mTabHost.getCurrentTabTag(); + Log.d(TAG, "switch state"); + switch (state.state) { + case AltosLib.ao_flight_boost: + if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent"); + break; + case AltosLib.ao_flight_drogue: + if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent"); + break; + case AltosLib.ao_flight_landed: + if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed"); + break; + case AltosLib.ao_flight_stateless: + if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent"); + break; + } + } + } + + AltosGreatCircle from_receiver = null; + + if (state != null && saved_location != null && state.gps != null && state.gps.locked) { + double altitude = 0; + if (saved_location.hasAltitude()) + altitude = saved_location.getAltitude(); + from_receiver = new AltosGreatCircle(saved_location.getLatitude(), + saved_location.getLongitude(), + altitude, + state.gps.lat, + state.gps.lon, + state.gps.alt); + } + + if (state != null) { + if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) { + Log.d(TAG, "update callsign"); + mCallsignView.setText(state.callsign); + } + if (saved_state == null || state.serial != saved_state.serial) { + Log.d(TAG, "update serial"); + mSerialView.setText(String.format("%d", state.serial)); + } + if (saved_state == null || state.flight != saved_state.flight) { + Log.d(TAG, "update flight"); + mFlightView.setText(String.format("%d", state.flight)); + } + if (saved_state == null || state.state != saved_state.state) { + Log.d(TAG, "update state"); + if (state.state == AltosLib.ao_flight_stateless) { + mStateLayout.setVisibility(View.GONE); + } else { + mStateView.setText(state.state_name()); + mStateLayout.setVisibility(View.VISIBLE); + } + } + if (saved_state == null || state.rssi != saved_state.rssi) { + Log.d(TAG, "update rssi"); + mRSSIView.setText(String.format("%d", state.rssi)); + } + } + + for (AltosDroidTab mTab : mTabs) + mTab.update_ui(state, from_receiver, saved_location, mTab == mTabsAdapter.currentItem()); + + if (state != null) + mAltosVoice.tell(state); + + saved_state = state; } - String pos(double p, String pos, String neg) { + private void onTimerTick() { + try { + mMessenger.send(Message.obtain(null, MSG_UPDATE_AGE)); + } catch (RemoteException e) { + } + } + + static String pos(double p, String pos, String neg) { String h = pos; + if (p == AltosLib.MISSING) + return ""; if (p < 0) { h = neg; p = -p; } int deg = (int) Math.floor(p); double min = (p - Math.floor(p)) * 60.0; - return String.format("%d° %9.6f\" %s", deg, min, h); + return String.format("%d°%9.4f\" %s", deg, min, h); + } + + static String number(String format, double value) { + if (value == AltosLib.MISSING) + return ""; + return String.format(format, value); + } + + static String integer(String format, int value) { + if (value == AltosLib.MISSING) + return ""; + return String.format(format, value); } @Override @@ -221,6 +382,8 @@ public class AltosDroid extends Activity { super.onCreate(savedInstanceState); if(D) Log.e(TAG, "+++ ON CREATE +++"); + fm = getSupportFragmentManager(); + // Get local Bluetooth adapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -232,39 +395,70 @@ public class AltosDroid extends Activity { } // Initialise preferences - prefs = new AltosDroidPreferences(this); - AltosPreferences.init(prefs); + AltosDroidPreferences.init(this); // Set up the window layout requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - //setContentView(R.layout.main); setContentView(R.layout.altosdroid); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); + // Create the Tabs and ViewPager + mTabHost = (TabHost)findViewById(android.R.id.tabhost); + mTabHost.setup(); + + mViewPager = (AltosViewPager)findViewById(R.id.pager); + mViewPager.setOffscreenPageLimit(4); + + mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager); + + mTabsAdapter.addTab(mTabHost.newTabSpec("pad").setIndicator("Pad"), TabPad.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("ascent").setIndicator("Ascent"), TabAscent.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("descent").setIndicator("Descent"), TabDescent.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("landed").setIndicator("Landed"), TabLanded.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null); + + + // Scale the size of the Tab bar for different screen densities + // This probably won't be needed when we start supporting ICS+ tabs. + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(metrics); + int density = metrics.densityDpi; + + if (density==DisplayMetrics.DENSITY_XHIGH) + tabHeight = 65; + else if (density==DisplayMetrics.DENSITY_HIGH) + tabHeight = 45; + else if (density==DisplayMetrics.DENSITY_MEDIUM) + tabHeight = 35; + else if (density==DisplayMetrics.DENSITY_LOW) + tabHeight = 25; + else + tabHeight = 65; + + for (int i = 0; i < 5; i++) + mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight; + + // Set up the custom title mTitle = (TextView) findViewById(R.id.title_left_text); mTitle.setText(R.string.app_name); mTitle = (TextView) findViewById(R.id.title_right_text); - // Set up the temporary Text View - mTextView = (TextView) findViewById(R.id.text); - mTextView.setMovementMethod(new ScrollingMovementMethod()); - mTextView.setClickable(false); - mTextView.setLongClickable(false); + // Display the Version + mVersion = (TextView) findViewById(R.id.version); + mVersion.setText("Version: " + BuildInfo.version + + " Built: " + BuildInfo.builddate + " " + BuildInfo.buildtime + " " + BuildInfo.buildtz + + " (" + BuildInfo.branch + "-" + BuildInfo.commitnum + "-" + BuildInfo.commithash + ")"); mCallsignView = (TextView) findViewById(R.id.callsign_value); mRSSIView = (TextView) findViewById(R.id.rssi_value); mSerialView = (TextView) findViewById(R.id.serial_value); mFlightView = (TextView) findViewById(R.id.flight_value); + mStateLayout = (RelativeLayout) findViewById(R.id.state_container); mStateView = (TextView) findViewById(R.id.state_value); - mSpeedView = (TextView) findViewById(R.id.speed_value); - mAccelView = (TextView) findViewById(R.id.accel_value); - mRangeView = (TextView) findViewById(R.id.range_value); - mHeightView = (TextView) findViewById(R.id.height_value); - mElevationView = (TextView) findViewById(R.id.elevation_value); - mBearingView = (TextView) findViewById(R.id.bearing_value); - mLatitudeView = (TextView) findViewById(R.id.latitude_value); - mLongitudeView = (TextView) findViewById(R.id.longitude_value); + mAgeView = (TextView) findViewById(R.id.age_value); + + timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 100L); mAltosVoice = new AltosVoice(this); } @@ -283,6 +477,7 @@ public class AltosDroid extends Activity { startService(new Intent(AltosDroid.this, TelemetryService.class)); doBindService(); + } @Override @@ -310,12 +505,9 @@ public class AltosDroid extends Activity { super.onDestroy(); if(D) Log.e(TAG, "--- ON DESTROY ---"); - mAltosVoice.stop(); + if (mAltosVoice != null) mAltosVoice.stop(); } - - - public void onActivityResult(int requestCode, int resultCode, Intent data) { if(D) Log.d(TAG, "onActivityResult " + resultCode); switch (requestCode) { @@ -341,9 +533,7 @@ public class AltosDroid extends Activity { } } - private void connectDevice(Intent data) { - // Get the device MAC address - String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); + private void connectDevice(String address) { // Get the BLuetoothDevice object BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); // Attempt to connect to the device @@ -354,6 +544,13 @@ public class AltosDroid extends Activity { } } + private void connectDevice(Intent data) { + // Get the device MAC address + String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); + AltosDroidPreferences.set_active_device(address); + connectDevice(address); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -375,6 +572,33 @@ public class AltosDroid extends Activity { } } + void setBaud(int baud) { + try { + mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud)); + } catch (RemoteException e) { + } + } + + void setBaud(String baud) { + try { + int value = Integer.parseInt(baud); + int rate = AltosLib.ao_telemetry_rate_38400; + switch (value) { + case 2400: + rate = AltosLib.ao_telemetry_rate_2400; + break; + case 9600: + rate = AltosLib.ao_telemetry_rate_9600; + break; + case 38400: + rate = AltosLib.ao_telemetry_rate_38400; + break; + } + setBaud(rate); + } catch (NumberFormatException e) { + } + } + @Override public boolean onOptionsItemSelected(MenuItem item) { Intent serverIntent = null; @@ -384,6 +608,11 @@ public class AltosDroid extends Activity { serverIntent = new Intent(this, DeviceListActivity.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); return true; + case R.id.quit: + Log.d(TAG, "R.id.quit"); + stopService(new Intent(AltosDroid.this, TelemetryService.class)); + finish(); + return true; case R.id.select_freq: // Set the TBT radio frequency @@ -400,16 +629,36 @@ public class AltosDroid extends Activity { "Channel 9 (435.450MHz)" }; - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Pick a frequency"); - builder.setItems(frequencies, + AlertDialog.Builder builder_freq = new AlertDialog.Builder(this); + builder_freq.setTitle("Pick a frequency"); + builder_freq.setItems(frequencies, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { setFrequency(frequencies[item]); } }); - AlertDialog alert = builder.create(); - alert.show(); + AlertDialog alert_freq = builder_freq.create(); + alert_freq.show(); + return true; + case R.id.select_rate: + // Set the TBT baud rate + + final String[] rates = { + "38400", + "9600", + "2400", + }; + + AlertDialog.Builder builder_rate = new AlertDialog.Builder(this); + builder_rate.setTitle("Pick a baud rate"); + builder_rate.setItems(rates, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + setBaud(rates[item]); + } + }); + AlertDialog alert_rate = builder_rate.create(); + alert_rate.show(); return true; } return false;